1 // dear imgui, v1.63 WIP
2 // (main code and documentation)
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
9 // 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
13 // 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
17 // to a better solution or official support for them.
24 - PROGRAMMER GUIDE (read me!)
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]
29 - API BREAKING CHANGES (read me when you update!)
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 use my own math types instead of ImVec2/ImVec4?
36 - How can I load a different font than the default?
37 - How can I easily use icons in my application?
38 - How can I load multiple fonts?
39 - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
40 - How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
41 - I integrated Dear ImGui in my engine and the text or lines are blurry..
42 - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
51 - Easy to use to create code-driven and data-driven tools
52 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools
53 - Easy to hack and improve
54 - Minimize screen real-estate usage
55 - Minimize setup and maintenance
56 - Minimize state storage on user side
57 - Portable, minimize dependencies, run on target (consoles, phones, etc.)
58 - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,
59 opening a tree node for the first time, etc. but a typical frame should not allocate anything)
61 Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
62 - Doesn't look fancy, doesn't animate
63 - Limited layout features, intricate layouts are typically crafted in code
69 - Double-click on title bar to collapse window.
70 - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
71 - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
72 - Click and drag on any empty space to move window.
73 - TAB/SHIFT+TAB to cycle through keyboard editable fields.
74 - CTRL+Click on a slider or drag box to input value as text.
75 - Use mouse wheel to scroll.
77 - Hold SHIFT or use mouse to select text.
78 - CTRL+Left/Right to word jump.
79 - CTRL+Shift+Left/Right to select words.
80 - CTRL+A our Double-Click to select all.
81 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
82 - CTRL+Z,CTRL+Y to undo/redo.
83 - ESCAPE to revert text to its original value.
84 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
85 - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
86 - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
87 - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW
95 - Read the FAQ below this section!
96 - 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
97 or destruction steps, less data retention on your side, less state duplication, less state synchronization, less bugs.
98 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
99 - You can learn about immediate-mode gui principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861
101 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
103 - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
104 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
105 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
106 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
107 likely be a comment about it. Please report any issue to the GitHub page!
108 - Try to keep your copy of dear imgui reasonably up to date.
110 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
112 - Run and study the examples and demo to get acquainted with the library.
113 - Add the Dear ImGui source files to your projects or using your preferred build system.
114 It is recommended you build the .cpp files as part of your project and not as a library.
115 - You can later customize the imconfig.h file to tweak some compilation time behavior, such as integrating imgui types with your own maths types.
116 - You may be able to grab and copy a ready made imgui_impl_*** file from the examples/ folder.
117 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
118 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
119 Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
120 phases of your own application. All rendering informatioe are stored into command-lists that you will retrieve after calling ImGui::Render().
121 - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
123 THIS IS HOW A SIMPLE APPLICATION MAY LOOK LIKE
124 EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder)
126 // Application init: create a dear imgui context, setup some options, load fonts
127 ImGui::CreateContext();
128 ImGuiIO& io = ImGui::GetIO();
129 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls
130 // TODO: Fill optional settings of the io structure later.
131 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
133 // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32 and imgui_impl_dx11)
134 ImGui_ImplWin32_Init(hwnd);
135 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
137 // Application main loop
140 // Feed inputs to dear imgui, start new frame
141 ImGui_ImplDX11_NewFrame();
142 ImGui_ImplWin32_NewFrame();
145 // Any application code here
146 ImGui::Text("Hello, world!");
148 // Render dear imgui into screen
150 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
151 g_pSwapChain->Present(1, 0);
155 ImGui_ImplDX11_Shutdown();
156 ImGui_ImplWin32_Shutdown();
157 ImGui::DestroyContext();
159 THIS IS HOW A SIMPLE APPLICATION MAY LOOK LIKE
160 EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE
162 // Application init: create a dear imgui context, setup some options, load fonts
163 ImGui::CreateContext();
164 ImGuiIO& io = ImGui::GetIO();
165 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls
166 // TODO: Fill optional settings of the io structure later.
167 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
169 // Build and load the texture atlas into a texture
170 // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
172 unsigned char* pixels = NULL;
173 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
175 // At this point you've got the texture data and you need to upload that your your graphic system:
176 // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
177 // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ below for details about ImTextureID.
178 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
179 io.Fonts->TexID = (void*)texture;
181 // Application main loop
184 // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
185 // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
186 io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds)
187 io.DisplaySize.x = 1920.0f; // set the current display width
188 io.DisplaySize.y = 1280.0f; // set the current display height here
189 io.MousePos = my_mouse_pos; // set the mouse position
190 io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states
191 io.MouseDown[1] = my_mouse_buttons[1];
193 // Call NewFrame(), after this point you can use ImGui::* functions anytime
194 // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use imgui everywhere)
197 // Most of your application code here
198 ImGui::Text("Hello, world!");
199 MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
200 MyGameRender(); // may use any ImGui functions as well!
202 // Render imgui, swap buffers
203 // (You want to try calling EndFrame/Render as late as you can, to be able to use imgui in your own game rendering code)
206 ImDrawData* draw_data = ImGui::GetDrawData();
207 MyImGuiRenderFunction(draw_data);
212 ImGui::DestroyContext();
214 THIS HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
216 void void MyImGuiRenderFunction(ImDrawData* draw_data)
218 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
219 // TODO: Setup viewport using draw_data->DisplaySize
220 // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
221 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
222 for (int n = 0; n < draw_data->CmdListsCount; n++)
224 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by ImGui
225 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by ImGui
226 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
228 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
229 if (pcmd->UserCallback)
231 pcmd->UserCallback(cmd_list, pcmd);
235 // The texture for the draw call is specified by pcmd->TextureId.
236 // The vast majority of draw calls with use the imgui texture atlas, which value you have set yourself during initialization.
237 MyEngineBindTexture(pcmd->TextureId);
239 // We are using scissoring to clip some objects. All low-level graphics API should supports it.
240 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
241 // (some elements visible outside their bounds) but you can fix that once everywhere else works!
242 // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
243 // In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
244 // However, in the interest of supporting multi-viewport applications in the future, always subtract draw_data->DisplayPos from
245 // clipping bounds to convert them to your viewport space.
246 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
247 ImVec2 pos = draw_data->DisplayPos;
248 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));
250 // Render 'pcmd->ElemCount/3' indexed triangles.
251 // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits if your engine doesn't support 16-bits indices.
252 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
254 idx_buffer += pcmd->ElemCount;
259 - The examples/ folders contains many functional implementation of the pseudo-code above.
260 - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
261 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.
262 In both cases you need to pass on the inputs to imgui. Read the FAQ below for more information about those flags.
263 - Please read the FAQ above. Amusingly, it is called a FAQ because people frequently have the same issues!
265 USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS [BETA]
267 - The gamepad/keyboard navigation is in Beta. Ask questions and report issues at https://github.com/ocornut/imgui/issues/787
268 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
270 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
271 - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
272 Note that io.NavInputs[] is cleared by EndFrame().
273 - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
274 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
275 - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
276 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.).
277 - You can download PNG/PSD files depicting the gamepad controls for common controllers at: goo.gl/9LgVZW.
278 - 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
279 to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
281 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
282 NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
283 - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
284 will be set. For more advanced uses, you may want to read from:
285 - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
286 - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
287 - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
288 Please reach out if you think the game vs navigation input sharing could be improved.
290 - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
291 - 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.
292 - 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.
293 Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
294 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.
295 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.
296 (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!)
297 (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
298 to set a boolean to ignore your other external mouse positions until the external source is moved again.)
304 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
305 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.
306 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.
307 You can read releases logs https://github.com/ocornut/imgui/releases for more details.
309 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
310 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
311 - 2018/07/06 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.OptResizeWindowsFromEdges to enable the feature.
312 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
313 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
314 - 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.
315 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
316 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.
317 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.
318 - 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",
319 consistent with other functions. Kept redirection functions (will obsolete).
320 - 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.
321 - 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).
322 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
323 - 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.
324 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
325 - 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.
326 - 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.
327 - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
328 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
329 - removed Shutdown() function, as DestroyContext() serve this purpose.
330 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
331 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
332 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
333 - 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.
334 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
335 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
336 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
337 - 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.
338 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
339 - 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
340 - 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.
341 - 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.
342 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
343 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
344 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
345 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
346 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
347 - 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.
348 - 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.
349 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.
350 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
351 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
352 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
353 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
354 - 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.
355 - 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.
356 - 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.
357 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
358 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
359 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
360 - 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).
361 - 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)".
362 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
363 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
364 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
365 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
366 - 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.
367 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame.
368 - 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.
369 - 2017/08/13 (1.51) - renamed ImGuiCol_Columns*** to ImGuiCol_Separator***. Kept redirection enums (will obsolete).
370 - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete).
371 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
372 - 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.
373 - 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.
374 - 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))'
375 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
376 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
377 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
378 - 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().
379 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
380 - 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.
381 - 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.
382 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
383 If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
384 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.
385 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.
386 ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
388 float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
389 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);
391 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.
392 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
393 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
394 - 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).
395 - 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.
396 - 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).
397 - 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)
398 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
399 - 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.
400 - 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.
401 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
402 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
403 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
404 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.
405 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!
406 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
407 - 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.
408 - 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
409 - 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.
410 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
411 - 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.
412 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
413 - 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.
414 - the signature of the io.RenderDrawListsFn handler has changed!
415 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
416 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
417 argument: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
418 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
419 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
420 - 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.
421 - 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!
422 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
423 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
424 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
425 - 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.
426 - 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
427 - 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!
428 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
429 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
430 - 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.
431 - 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.
432 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
433 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
434 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
435 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
436 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
437 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
438 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
439 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
440 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
441 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
442 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
443 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
444 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
445 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
446 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
447 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
448 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
449 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
450 font init: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..>
451 became: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier;
452 you now more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
453 it is now recommended that you sample the font texture with bilinear interpolation.
454 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
455 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
456 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
457 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
458 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
459 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
460 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
461 - 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)
462 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
463 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
464 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
465 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
466 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
467 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
475 FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
476 ======================================
478 Q: How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
479 A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure (e.g. if (ImGui::GetIO().WantCaptureMouse) { ... } )
480 - 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.
481 - 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.
482 - 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).
483 Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false.
484 This is because imgui needs to detect that you clicked in the void to unfocus its windows.
485 Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!).
486 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.
487 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
488 perfectly fine, as the bool toggle fairly rarely. If you have on a touch device, you might find use for an early call to UpdateHoveredWindowAndCaptureFlags().
489 Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically
490 have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
491 were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
493 Q: How can I display an image? What is ImTextureID, how does it works?
494 A: Short explanation:
495 - You may use functions such as ImGui::Image(), ImGui::ImageButton() or lower-level ImDrawList::AddImage() to emit draw calls that will use your own textures.
496 - Actual textures are identified in a way that is up to the user/engine.
497 - Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason).
498 Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward.
501 - Dear ImGui's job is to create "meshes", defined in a renderer-agnostic format made of draw commands and vertices.
502 At the end of the frame those meshes (ImDrawList) will be displayed by your rendering function. They are made up of textured polygons and the code
503 to render them is generally fairly short (a few dozen lines). In the examples/ folder we provide functions for popular graphics API (OpenGL, DirectX, etc.).
504 - Each rendering function decides on a data type to represent "textures". The concept of what is a "texture" is entirely tied to your underlying engine/graphics API.
505 We carry the information to identify a "texture" in the ImTextureID type.
506 ImTextureID is nothing more that a void*, aka 4/8 bytes worth of data: just enough to store 1 pointer or 1 integer of your choice.
507 Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely pass ImTextureID values until they reach your rendering function.
508 - In the examples/ bindings, for each graphics API binding we decided on a type that is likely to be a good representation for specifying
509 an image from the end-user perspective. This is what the _examples_ rendering functions are using:
511 OpenGL: ImTextureID = GLuint (see ImGui_ImplGlfwGL3_RenderDrawData() function in imgui_impl_glfw_gl3.cpp)
512 DirectX9: ImTextureID = LPDIRECT3DTEXTURE9 (see ImGui_ImplDX9_RenderDrawData() function in imgui_impl_dx9.cpp)
513 DirectX11: ImTextureID = ID3D11ShaderResourceView* (see ImGui_ImplDX11_RenderDrawData() function in imgui_impl_dx11.cpp)
514 DirectX12: ImTextureID = D3D12_GPU_DESCRIPTOR_HANDLE (see ImGui_ImplDX12_RenderDrawData() function in imgui_impl_dx12.cpp)
516 For example, in the OpenGL example binding we store raw OpenGL texture identifier (GLuint) inside ImTextureID.
517 Whereas in the DirectX11 example binding we store a pointer to ID3D11ShaderResourceView inside ImTextureID, which is a higher-level structure
518 tying together both the texture and information about its format and how to read it.
519 - If you have a custom engine built over e.g. OpenGL, instead of passing GLuint around you may decide to use a high-level data type to carry information about
520 the texture as well as how to display it (shaders, etc.). The decision of what to use as ImTextureID can always be made better knowing how your codebase
521 is designed. If your engine has high-level data types for "textures" and "material" then you may want to use them.
522 If you are starting with OpenGL or DirectX or Vulkan and haven't built much of a rendering engine over them, keeping the default ImTextureID
523 representation suggested by the example bindings is probably the best choice.
524 (Advanced users may also decide to keep a low-level type in ImTextureID, and use ImDrawList callback and pass information to their renderer)
528 // Cast our texture type to ImTextureID / void*
529 MyTexture* texture = g_CoffeeTableTexture;
530 ImGui::Image((void*)texture, ImVec2(texture->Width, texture->Height));
532 The renderer function called after ImGui::Render() will receive that same value that the user code passed:
534 // Cast ImTextureID / void* stored in the draw command as our texture type
535 MyTexture* texture = (MyTexture*)pcmd->TextureId;
536 MyEngineBindTexture2D(texture);
538 Once you understand this design you will understand that loading image files and turning them into displayable textures is not within the scope of Dear ImGui.
539 This is by design and is actually a good thing, because it means your code has full control over your data types and how you display them.
540 If you want to display an image file (e.g. PNG file) into the screen, please refer to documentation and tutorials for the graphics API you are using.
542 Here's a simplified OpenGL example using stb_image.h:
544 // Use stb_image.h to load a PNG from disk and turn it into raw RGBA pixel data:
545 #define STB_IMAGE_IMPLEMENTATION
546 #include <stb_image.h>
548 int my_image_width, my_image_height;
549 unsigned char* my_image_data = stbi_load("my_image.png", &my_image_width, &my_image_height, NULL, 4);
551 // Turn the RGBA pixel data into an OpenGL texture:
552 GLuint my_opengl_texture;
553 glGenTextures(1, &my_opengl_texture);
554 glBindTexture(GL_TEXTURE_2D, my_opengl_texture);
555 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
556 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
558 // Now that we have an OpenGL texture, assuming our imgui rendering function (imgui_impl_xxx.cpp file) takes GLuint as ImTextureID, we can display it:
559 ImGui::Image((void*)(intptr_t)my_opengl_texture, ImVec2(my_image_width, my_image_height));
561 C/C++ tip: a void* is pointer-sized storage. You may safely store any pointer or integer into it by casting your value to ImTexture / void*, and vice-versa.
562 Because both end-points (user code and rendering function) are under your control, you know exactly what is stored inside the ImTexture / void*.
567 my_void_ptr = (void*)(intptr_t)my_tex; // cast a GLuint into a void* (we don't take its address! we literally store the value inside the pointer)
568 my_tex = (GLuint)(intptr_t)my_void_ptr; // cast a void* into a GLuint
570 ID3D11ShaderResourceView* my_dx11_srv = XXX;
572 my_void_ptr = (void*)my_dx11_srv; // cast a ID3D11ShaderResourceView* into an opaque void*
573 my_dx11_srv = (ID3D11ShaderResourceView*)my_void_ptr; // cast a void* into a ID3D11ShaderResourceView*
575 Finally, you may call ImGui::ShowMetricsWindow() to explore/visualize/understand how the ImDrawList are generated.
577 Q: How can I have multiple widgets with the same label or without a label?
578 Q: I have multiple widgets with the same label, and only the first one works. Why is that?
579 A: A primer on labels and the ID Stack...
581 Dear ImGui internally need to uniquely identify UI elements.
582 Elements that are typically not clickable (such as calls to the Text functions) don't need an ID.
583 Interactive widgets (such as calls to Button buttons) need a unique ID.
584 Unique ID are used internally to track active widgets and occasionally associate state to widgets.
585 Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element.
587 - Unique ID are often derived from a string label:
589 Button("OK"); // Label = "OK", ID = hash of (..., "OK")
590 Button("Cancel"); // Label = "Cancel", ID = hash of (..., "Cancel")
592 - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having
593 two buttons labeled "OK" in different windows or different tree locations is fine.
594 We used "..." above to signify whatever was already pushed to the ID stack previously:
597 Button("OK"); // Label = "OK", ID = hash of ("MyWindow", "OK")
600 - If you have a same ID twice in the same location, you'll have a conflict:
603 Button("OK"); // ID collision! Interacting with either button will trigger the first one.
605 Fear not! this is easy to solve and there are many ways to solve it!
607 - Solving ID conflict in a simple/local context:
608 When passing a label you can optionally specify extra ID information within string itself.
609 Use "##" to pass a complement to the ID that won't be visible to the end-user.
610 This helps solving the simple collision cases when you know e.g. at compilation time which items
611 are going to be created:
614 Button("Play"); // Label = "Play", ID = hash of ("MyWindow", "Play")
615 Button("Play##foo1"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo1") // Different from above
616 Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo2") // Different from above
619 - If you want to completely hide the label, but still need an ID:
621 Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label!
623 - Occasionally/rarely you might want change a label while preserving a constant ID. This allows
624 you to animate labels. For example you may want to include varying information in a window title bar,
625 but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
627 Button("Hello###ID"; // Label = "Hello", ID = hash of (..., "ID")
628 Button("World###ID"; // Label = "World", ID = hash of (..., "ID") // Same as above, even though the label looks different
630 sprintf(buf, "My game (%f FPS)###MyGame", fps);
631 Begin(buf); // Variable label, ID = hash of "MyGame"
633 - Solving ID conflict in a more general manner:
634 Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts
635 within the same window. This is the most convenient way of distinguishing ID when iterating and
636 creating many UI elements programmatically.
637 You can push a pointer, a string or an integer value into the ID stack.
638 Remember that ID are formed from the concatenation of _everything_ in the ID stack!
641 for (int i = 0; i < 100; i++)
643 PushID(i); // Push i to the id tack
644 Button("Click"); // Label = "Click", ID = Hash of ("Window", i, "Click")
647 for (int i = 0; i < 100; i++)
649 MyObject* obj = Objects[i];
651 Button("Click"); // Label = "Click", ID = Hash of ("Window", obj pointer, "Click")
654 for (int i = 0; i < 100; i++)
656 MyObject* obj = Objects[i];
658 Button("Click"); // Label = "Click", ID = Hash of ("Window", obj->Name, "Click")
663 - More example showing that you can stack multiple prefixes into the ID stack:
665 Button("Click"); // Label = "Click", ID = hash of (..., "Click")
667 Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
669 Button("Click"); // Label = "Click", ID = hash of (..., "node", my_ptr, "Click")
673 - Tree nodes implicitly creates a scope for you by calling PushID().
675 Button("Click"); // Label = "Click", ID = hash of (..., "Click")
676 if (TreeNode("node"))
678 Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
682 - When working with trees, ID are used to preserve the open/close state of each tree node.
683 Depending on your use cases you may want to use strings, indices or pointers as ID.
684 e.g. when following a single pointer that may change over time, using a static string as ID
685 will preserve your node open/closed state when the targeted object change.
686 e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
687 node open/closed state differently. See what makes more sense in your situation!
689 Q: How can I use my own math types instead of ImVec2/ImVec4?
690 A: You can edit imconfig.h and setup the IM_VEC2_CLASS_EXTRA/IM_VEC4_CLASS_EXTRA macros to add implicit type conversions.
691 This way you'll be able to use your own types everywhere, e.g. passsing glm::vec2 to ImGui functions instead of ImVec2.
693 Q: How can I load a different font than the default?
694 A: Use the font atlas to load the TTF/OTF file you want:
695 ImGuiIO& io = ImGui::GetIO();
696 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
697 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
698 (default is ProggyClean.ttf, rendered at size 13, embedded in dear imgui's source code)
700 New programmers: remember that in C/C++ and most programming languages if you want to use a
701 backslash \ within a string literal, you need to write it double backslash "\\":
702 io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels); // WRONG (you are escape the M here!)
703 io.Fonts->AddFontFromFileTTF("MyDataFolder\\MyFontFile.ttf", size_in_pixels); // CORRECT
704 io.Fonts->AddFontFromFileTTF("MyDataFolder/MyFontFile.ttf", size_in_pixels); // ALSO CORRECT
706 Q: How can I easily use icons in my application?
707 A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you
708 main font. Then you can refer to icons within your strings. Read 'How can I load multiple fonts?'
709 and the file 'misc/fonts/README.txt' for instructions and useful header files.
711 Q: How can I load multiple fonts?
712 A: Use the font atlas to pack them into a single texture:
713 (Read misc/fonts/README.txt and the code in ImFontAtlas for more details.)
715 ImGuiIO& io = ImGui::GetIO();
716 ImFont* font0 = io.Fonts->AddFontDefault();
717 ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
718 ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
719 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
720 // the first loaded font gets used by default
721 // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
725 config.OversampleH = 3;
726 config.OversampleV = 1;
727 config.GlyphOffset.y -= 2.0f; // Move everything by 2 pixels up
728 config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters
729 io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config);
731 // Combine multiple fonts into one (e.g. for icon fonts)
732 ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
734 config.MergeMode = true;
735 io.Fonts->AddFontDefault();
736 io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font
737 io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs
739 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
740 A: When loading a font, pass custom Unicode ranges to specify the glyphs to load.
742 // Add default Japanese ranges
743 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());
745 // 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)
746 ImVector<ImWchar> ranges;
747 ImFontAtlas::GlyphRangesBuilder builder;
748 builder.AddText("Hello world"); // Add a string (here "Hello world" contains 7 unique characters)
749 builder.AddChar(0x7262); // Add a specific character
750 builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges
751 builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted)
752 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data);
754 All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8
755 by using the u8"hello" syntax. Specifying literal in your source code using a local code page
756 (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work!
757 Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
759 Text input: it is up to your application to pass the right character code by calling io.AddInputCharacter().
760 The applications in examples/ are doing that.
761 Windows: you can use the WM_CHAR or WM_UNICHAR or WM_IME_CHAR message (depending if your app is built using Unicode or MultiByte mode).
762 You may also use MultiByteToWideChar() or ToUnicode() to retrieve Unicode codepoints from MultiByte characters or keyboard state.
763 Windows: if your language is relying on an Input Method Editor (IME), you copy the HWND of your window to io.ImeWindowHandle in order for
764 the default implementation of io.ImeSetInputScreenPosFn() to set your Microsoft IME position correctly.
766 Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
767 A: - You can create a dummy window. Call SetNextWindowBgAlpha(0.0f), call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flags.
768 Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
769 - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows.
770 - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create your own ImDrawListSharedData.
772 Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
773 A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
774 Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
776 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
777 A: You are probably mishandling the clipping rectangles in your render function.
778 Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
781 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!
782 - Convince your company to fund development time! Individual users: you can also become a Patron (patreon.com/imgui) or donate on PayPal! See README.
783 - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
784 You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1269). Visuals are ideal as they inspire other programmers.
785 But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
786 - 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).
788 - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window.
789 this is also useful to set yourself in the context of another window (to get/set other settings)
790 - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug".
791 - 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
792 of a deep nested inner loop in your code.
793 - tip: you can call Render() multiple times (e.g for VR renders).
794 - tip: call and read the ShowDemoWindow() code in imgui_demo.cpp for more example of how to use ImGui!
798 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
799 #define _CRT_SECURE_NO_WARNINGS
803 #ifndef IMGUI_DEFINE_MATH_OPERATORS
804 #define IMGUI_DEFINE_MATH_OPERATORS
806 #include "imgui_internal.h"
808 #include <ctype.h> // toupper, isprint
809 #include <stdio.h> // vsnprintf, sscanf, printf
810 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
811 #include <stddef.h> // intptr_t
813 #include <stdint.h> // intptr_t
816 #define IMGUI_DEBUG_NAV_SCORING 0
817 #define IMGUI_DEBUG_NAV_RECTS 0
819 // Visual Studio warnings
821 #pragma warning (disable: 4127) // condition expression is constant
822 #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
823 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
826 // Clang warnings with -Weverything
828 #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!
829 #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
830 #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.
831 #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.
832 #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.
833 #pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it.
834 #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
835 #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.
836 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int'
837 #elif defined(__GNUC__)
838 #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
839 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
840 #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
841 #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
842 #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
843 #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
844 #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
846 #pragma GCC diagnostic ignored "-Wclass-memaccess" // warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
850 static const ImS32 IM_S32_MIN
= 0x80000000; // INT_MIN;
851 static const ImS32 IM_S32_MAX
= 0x7FFFFFFF; // INT_MAX;
852 static const ImU32 IM_U32_MIN
= 0;
853 static const ImU32 IM_U32_MAX
= 0xFFFFFFFF;
854 static const ImS64 IM_S64_MIN
= -9223372036854775807ll - 1ll;
855 static const ImS64 IM_S64_MAX
= 9223372036854775807ll;
856 static const ImU64 IM_U64_MIN
= 0;
857 static const ImU64 IM_U64_MAX
= 0xFFFFFFFFFFFFFFFFull
;
859 // 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.
860 static const float NAV_WINDOWING_HIGHLIGHT_DELAY
= 0.20f
; // Time before the highlight and screen dimming starts fading in
861 static const float NAV_WINDOWING_LIST_APPEAR_DELAY
= 0.15f
; // Time before the window list starts to appear
863 //-------------------------------------------------------------------------
864 // Forward Declarations
865 //-------------------------------------------------------------------------
867 static bool IsKeyPressedMap(ImGuiKey key
, bool repeat
= true);
869 static void SetCurrentWindow(ImGuiWindow
* window
);
870 static void SetWindowScrollX(ImGuiWindow
* window
, float new_scroll_x
);
871 static void SetWindowScrollY(ImGuiWindow
* window
, float new_scroll_y
);
872 static void SetWindowPos(ImGuiWindow
* window
, const ImVec2
& pos
, ImGuiCond cond
);
873 static void SetWindowSize(ImGuiWindow
* window
, const ImVec2
& size
, ImGuiCond cond
);
874 static void SetWindowCollapsed(ImGuiWindow
* window
, bool collapsed
, ImGuiCond cond
);
875 static void FindHoveredWindow();
876 static ImGuiWindow
* CreateNewWindow(const char* name
, ImVec2 size
, ImGuiWindowFlags flags
);
877 static ImGuiWindowSettings
* CreateNewWindowSettings(const char* name
);
878 static void CheckStacksSize(ImGuiWindow
* window
, bool write
);
879 static ImVec2
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow
* window
, bool snap_on_edges
);
881 static void AddDrawListToDrawData(ImVector
<ImDrawList
*>* out_list
, ImDrawList
* draw_list
);
882 static void AddWindowToDrawData(ImVector
<ImDrawList
*>* out_list
, ImGuiWindow
* window
);
883 static void AddWindowToSortedBuffer(ImVector
<ImGuiWindow
*>* out_sorted_windows
, ImGuiWindow
* window
);
885 static ImRect
GetViewportRect();
887 static bool InputTextFilterCharacter(unsigned int* p_char
, ImGuiInputTextFlags flags
, ImGuiTextEditCallback callback
, void* user_data
);
888 static int InputTextCalcTextLenAndLineCount(const char* text_begin
, const char** out_text_end
);
889 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);
891 static inline int DataTypeFormatString(char* buf
, int buf_size
, ImGuiDataType data_type
, const void* data_ptr
, const char* format
);
892 static void DataTypeApplyOp(ImGuiDataType data_type
, int op
, void* output
, void* arg_1
, const void* arg_2
);
893 static bool DataTypeApplyOpFromText(const char* buf
, const char* initial_value_buf
, ImGuiDataType data_type
, void* data_ptr
, const char* format
);
897 static bool BeginChildEx(const char* name
, ImGuiID id
, const ImVec2
& size_arg
, bool border
, ImGuiWindowFlags flags
);
899 static void NavUpdate();
900 static void NavUpdateWindowing();
901 static void NavUpdateWindowingList();
902 static void NavProcessItem(ImGuiWindow
* window
, const ImRect
& nav_bb
, const ImGuiID id
);
904 static void UpdateMouseInputs();
905 static void UpdateMouseWheel();
906 static void UpdateManualResize(ImGuiWindow
* window
, const ImVec2
& size_auto_fit
, int* border_held
, int resize_grip_count
, ImU32 resize_grip_col
[4]);
907 static void FocusFrontMostActiveWindow(ImGuiWindow
* ignore_window
);
909 // Template widget behaviors
910 template<typename TYPE
, typename SIGNEDTYPE
, typename FLOATTYPE
>
911 static bool DragBehaviorT(ImGuiDataType data_type
, TYPE
* v
, float v_speed
, const TYPE v_min
, const TYPE v_max
, const char* format
, float power
);
913 template<typename TYPE
, typename SIGNEDTYPE
, typename FLOATTYPE
>
914 static bool SliderBehaviorT(const ImRect
& bb
, ImGuiID id
, ImGuiDataType data_type
, TYPE
* v
, const TYPE v_min
, const TYPE v_max
, const char* format
, float power
, ImGuiSliderFlags flags
);
917 //-----------------------------------------------------------------------------
918 // Platform dependent default implementations
919 //-----------------------------------------------------------------------------
921 static const char* GetClipboardTextFn_DefaultImpl(void* user_data
);
922 static void SetClipboardTextFn_DefaultImpl(void* user_data
, const char* text
);
923 static void ImeSetInputScreenPosFn_DefaultImpl(int x
, int y
);
925 //-----------------------------------------------------------------------------
927 //-----------------------------------------------------------------------------
929 // Current context pointer. Implicitly used by all ImGui functions. Always assumed to be != NULL.
930 // CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
931 // If you use DLL hotreloading you might need to call SetCurrentContext() after reloading code from this file.
932 // 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:
933 // - 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
934 // - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts)
936 ImGuiContext
* GImGui
= NULL
;
939 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
940 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
941 // 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.
942 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
943 static void* MallocWrapper(size_t size
, void* user_data
) { (void)user_data
; return malloc(size
); }
944 static void FreeWrapper(void* ptr
, void* user_data
) { (void)user_data
; free(ptr
); }
946 static void* MallocWrapper(size_t size
, void* user_data
) { (void)user_data
; (void)size
; IM_ASSERT(0); return NULL
; }
947 static void FreeWrapper(void* ptr
, void* user_data
) { (void)user_data
; (void)ptr
; IM_ASSERT(0); }
950 static void* (*GImAllocatorAllocFunc
)(size_t size
, void* user_data
) = MallocWrapper
;
951 static void (*GImAllocatorFreeFunc
)(void* ptr
, void* user_data
) = FreeWrapper
;
952 static void* GImAllocatorUserData
= NULL
;
953 static size_t GImAllocatorActiveAllocationsCount
= 0;
955 //-----------------------------------------------------------------------------
956 // User facing structures
957 //-----------------------------------------------------------------------------
959 ImGuiStyle::ImGuiStyle()
961 Alpha
= 1.0f
; // Global alpha applies to everything in ImGui
962 WindowPadding
= ImVec2(8,8); // Padding within a window
963 WindowRounding
= 7.0f
; // Radius of window corners rounding. Set to 0.0f to have rectangular windows
964 WindowBorderSize
= 1.0f
; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
965 WindowMinSize
= ImVec2(32,32); // Minimum window size
966 WindowTitleAlign
= ImVec2(0.0f
,0.5f
);// Alignment for title bar text
967 ChildRounding
= 0.0f
; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
968 ChildBorderSize
= 1.0f
; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
969 PopupRounding
= 0.0f
; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
970 PopupBorderSize
= 1.0f
; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
971 FramePadding
= ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
972 FrameRounding
= 0.0f
; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
973 FrameBorderSize
= 0.0f
; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
974 ItemSpacing
= ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
975 ItemInnerSpacing
= ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
976 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!
977 IndentSpacing
= 21.0f
; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
978 ColumnsMinSpacing
= 6.0f
; // Minimum horizontal spacing between two columns
979 ScrollbarSize
= 16.0f
; // Width of the vertical scrollbar, Height of the horizontal scrollbar
980 ScrollbarRounding
= 9.0f
; // Radius of grab corners rounding for scrollbar
981 GrabMinSize
= 10.0f
; // Minimum width/height of a grab box for slider/scrollbar
982 GrabRounding
= 0.0f
; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
983 ButtonTextAlign
= ImVec2(0.5f
,0.5f
);// Alignment of button text when button is larger than text.
984 DisplayWindowPadding
= ImVec2(20,20); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows.
985 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.
986 MouseCursorScale
= 1.0f
; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
987 AntiAliasedLines
= true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
988 AntiAliasedFill
= true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
989 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.
992 ImGui::StyleColorsDark(this);
995 // To scale your entire UI (e.g. if you want your app to use High DPI or generally be DPI aware) you may use this helper function. Scaling the fonts is done separately and is up to you.
996 // Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times.
997 void ImGuiStyle::ScaleAllSizes(float scale_factor
)
999 WindowPadding
= ImFloor(WindowPadding
* scale_factor
);
1000 WindowRounding
= ImFloor(WindowRounding
* scale_factor
);
1001 WindowMinSize
= ImFloor(WindowMinSize
* scale_factor
);
1002 ChildRounding
= ImFloor(ChildRounding
* scale_factor
);
1003 PopupRounding
= ImFloor(PopupRounding
* scale_factor
);
1004 FramePadding
= ImFloor(FramePadding
* scale_factor
);
1005 FrameRounding
= ImFloor(FrameRounding
* scale_factor
);
1006 ItemSpacing
= ImFloor(ItemSpacing
* scale_factor
);
1007 ItemInnerSpacing
= ImFloor(ItemInnerSpacing
* scale_factor
);
1008 TouchExtraPadding
= ImFloor(TouchExtraPadding
* scale_factor
);
1009 IndentSpacing
= ImFloor(IndentSpacing
* scale_factor
);
1010 ColumnsMinSpacing
= ImFloor(ColumnsMinSpacing
* scale_factor
);
1011 ScrollbarSize
= ImFloor(ScrollbarSize
* scale_factor
);
1012 ScrollbarRounding
= ImFloor(ScrollbarRounding
* scale_factor
);
1013 GrabMinSize
= ImFloor(GrabMinSize
* scale_factor
);
1014 GrabRounding
= ImFloor(GrabRounding
* scale_factor
);
1015 DisplayWindowPadding
= ImFloor(DisplayWindowPadding
* scale_factor
);
1016 DisplaySafeAreaPadding
= ImFloor(DisplaySafeAreaPadding
* scale_factor
);
1017 MouseCursorScale
= ImFloor(MouseCursorScale
* scale_factor
);
1022 // Most fields are initialized with zero
1023 memset(this, 0, sizeof(*this));
1027 BackendFlags
= 0x00;
1028 DisplaySize
= ImVec2(-1.0f
, -1.0f
);
1029 DeltaTime
= 1.0f
/60.0f
;
1030 IniSavingRate
= 5.0f
;
1031 IniFilename
= "imgui.ini";
1032 LogFilename
= "imgui_log.txt";
1033 MouseDoubleClickTime
= 0.30f
;
1034 MouseDoubleClickMaxDist
= 6.0f
;
1035 for (int i
= 0; i
< ImGuiKey_COUNT
; i
++)
1037 KeyRepeatDelay
= 0.250f
;
1038 KeyRepeatRate
= 0.050f
;
1042 FontGlobalScale
= 1.0f
;
1044 FontAllowUserScaling
= false;
1045 DisplayFramebufferScale
= ImVec2(1.0f
, 1.0f
);
1046 DisplayVisibleMin
= DisplayVisibleMax
= ImVec2(0.0f
, 0.0f
);
1048 // Advanced/subtle behaviors
1050 OptMacOSXBehaviors
= true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1052 OptMacOSXBehaviors
= false;
1054 OptCursorBlink
= true;
1056 // Settings (User Functions)
1057 GetClipboardTextFn
= GetClipboardTextFn_DefaultImpl
; // Platform dependent default implementations
1058 SetClipboardTextFn
= SetClipboardTextFn_DefaultImpl
;
1059 ClipboardUserData
= NULL
;
1060 ImeSetInputScreenPosFn
= ImeSetInputScreenPosFn_DefaultImpl
;
1061 ImeWindowHandle
= NULL
;
1063 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1064 RenderDrawListsFn
= NULL
;
1067 // Input (NB: we already have memset zero the entire structure)
1068 MousePos
= ImVec2(-FLT_MAX
, -FLT_MAX
);
1069 MousePosPrev
= ImVec2(-FLT_MAX
, -FLT_MAX
);
1070 MouseDragThreshold
= 6.0f
;
1071 for (int i
= 0; i
< IM_ARRAYSIZE(MouseDownDuration
); i
++) MouseDownDuration
[i
] = MouseDownDurationPrev
[i
] = -1.0f
;
1072 for (int i
= 0; i
< IM_ARRAYSIZE(KeysDownDuration
); i
++) KeysDownDuration
[i
] = KeysDownDurationPrev
[i
] = -1.0f
;
1073 for (int i
= 0; i
< IM_ARRAYSIZE(NavInputsDownDuration
); i
++) NavInputsDownDuration
[i
] = -1.0f
;
1076 // Pass in translated ASCII characters for text input.
1077 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1078 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
1079 void ImGuiIO::AddInputCharacter(ImWchar c
)
1081 const int n
= ImStrlenW(InputCharacters
);
1082 if (n
+ 1 < IM_ARRAYSIZE(InputCharacters
))
1084 InputCharacters
[n
] = c
;
1085 InputCharacters
[n
+1] = '\0';
1089 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars
)
1091 // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more
1092 const int wchars_buf_len
= sizeof(ImGuiIO::InputCharacters
) / sizeof(ImWchar
);
1093 ImWchar wchars
[wchars_buf_len
];
1094 ImTextStrFromUtf8(wchars
, wchars_buf_len
, utf8_chars
, NULL
);
1095 for (int i
= 0; i
< wchars_buf_len
&& wchars
[i
] != 0; i
++)
1096 AddInputCharacter(wchars
[i
]);
1099 //-----------------------------------------------------------------------------
1101 //-----------------------------------------------------------------------------
1103 #define IM_STATIC_ASSERT(_COND) typedef char static_assertion_##__line__[(_COND)?1:-1]
1104 #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose
1105 #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255
1107 ImVec2
ImLineClosestPoint(const ImVec2
& a
, const ImVec2
& b
, const ImVec2
& p
)
1110 ImVec2 ab_dir
= b
- a
;
1111 float dot
= ap
.x
* ab_dir
.x
+ ap
.y
* ab_dir
.y
;
1114 float ab_len_sqr
= ab_dir
.x
* ab_dir
.x
+ ab_dir
.y
* ab_dir
.y
;
1115 if (dot
> ab_len_sqr
)
1117 return a
+ ab_dir
* dot
/ ab_len_sqr
;
1120 bool ImTriangleContainsPoint(const ImVec2
& a
, const ImVec2
& b
, const ImVec2
& c
, const ImVec2
& p
)
1122 bool b1
= ((p
.x
- b
.x
) * (a
.y
- b
.y
) - (p
.y
- b
.y
) * (a
.x
- b
.x
)) < 0.0f
;
1123 bool b2
= ((p
.x
- c
.x
) * (b
.y
- c
.y
) - (p
.y
- c
.y
) * (b
.x
- c
.x
)) < 0.0f
;
1124 bool b3
= ((p
.x
- a
.x
) * (c
.y
- a
.y
) - (p
.y
- a
.y
) * (c
.x
- a
.x
)) < 0.0f
;
1125 return ((b1
== b2
) && (b2
== b3
));
1128 void ImTriangleBarycentricCoords(const ImVec2
& a
, const ImVec2
& b
, const ImVec2
& c
, const ImVec2
& p
, float& out_u
, float& out_v
, float& out_w
)
1133 const float denom
= v0
.x
* v1
.y
- v1
.x
* v0
.y
;
1134 out_v
= (v2
.x
* v1
.y
- v1
.x
* v2
.y
) / denom
;
1135 out_w
= (v0
.x
* v2
.y
- v2
.x
* v0
.y
) / denom
;
1136 out_u
= 1.0f
- out_v
- out_w
;
1139 ImVec2
ImTriangleClosestPoint(const ImVec2
& a
, const ImVec2
& b
, const ImVec2
& c
, const ImVec2
& p
)
1141 ImVec2 proj_ab
= ImLineClosestPoint(a
, b
, p
);
1142 ImVec2 proj_bc
= ImLineClosestPoint(b
, c
, p
);
1143 ImVec2 proj_ca
= ImLineClosestPoint(c
, a
, p
);
1144 float dist2_ab
= ImLengthSqr(p
- proj_ab
);
1145 float dist2_bc
= ImLengthSqr(p
- proj_bc
);
1146 float dist2_ca
= ImLengthSqr(p
- proj_ca
);
1147 float m
= ImMin(dist2_ab
, ImMin(dist2_bc
, dist2_ca
));
1155 int ImStricmp(const char* str1
, const char* str2
)
1158 while ((d
= toupper(*str2
) - toupper(*str1
)) == 0 && *str1
) { str1
++; str2
++; }
1162 int ImStrnicmp(const char* str1
, const char* str2
, size_t count
)
1165 while (count
> 0 && (d
= toupper(*str2
) - toupper(*str1
)) == 0 && *str1
) { str1
++; str2
++; count
--; }
1169 void ImStrncpy(char* dst
, const char* src
, size_t count
)
1171 if (count
< 1) return;
1172 strncpy(dst
, src
, count
);
1176 char* ImStrdup(const char *str
)
1178 size_t len
= strlen(str
) + 1;
1179 void* buf
= ImGui::MemAlloc(len
);
1180 return (char*)memcpy(buf
, (const void*)str
, len
);
1183 const char* ImStrchrRange(const char* str
, const char* str_end
, char c
)
1185 for ( ; str
< str_end
; str
++)
1191 int ImStrlenW(const ImWchar
* str
)
1198 const ImWchar
* ImStrbolW(const ImWchar
* buf_mid_line
, const ImWchar
* buf_begin
) // find beginning-of-line
1200 while (buf_mid_line
> buf_begin
&& buf_mid_line
[-1] != '\n')
1202 return buf_mid_line
;
1205 const char* ImStristr(const char* haystack
, const char* haystack_end
, const char* needle
, const char* needle_end
)
1208 needle_end
= needle
+ strlen(needle
);
1210 const char un0
= (char)toupper(*needle
);
1211 while ((!haystack_end
&& *haystack
) || (haystack_end
&& haystack
< haystack_end
))
1213 if (toupper(*haystack
) == un0
)
1215 const char* b
= needle
+ 1;
1216 for (const char* a
= haystack
+ 1; b
< needle_end
; a
++, b
++)
1217 if (toupper(*a
) != toupper(*b
))
1219 if (b
== needle_end
)
1227 // 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.
1228 void ImStrTrimBlanks(char* buf
)
1231 while (p
[0] == ' ' || p
[0] == '\t') // Leading blanks
1234 while (*p
!= 0) // Find end of string
1236 while (p
> p_start
&& (p
[-1] == ' ' || p
[-1] == '\t')) // Trailing blanks
1238 if (p_start
!= buf
) // Copy memory if we had leading blanks
1239 memmove(buf
, p_start
, p
- p_start
);
1240 buf
[p
- p_start
] = 0; // Zero terminate
1243 template<typename TYPE
>
1244 static const char* ImAtoi(const char* src
, TYPE
* output
)
1247 if (*src
== '-') { negative
= 1; src
++; }
1248 if (*src
== '+') { src
++; }
1250 while (*src
>= '0' && *src
<= '9')
1251 v
= (v
* 10) + (*src
++ - '0');
1252 *output
= negative
? -v
: v
;
1256 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1257 // 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.
1258 // B) When buf==NULL vsnprintf() will return the output size.
1259 #ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1260 int ImFormatString(char* buf
, size_t buf_size
, const char* fmt
, ...)
1262 IM_ASSERT(fmt
!= NULL
);
1264 va_start(args
, fmt
);
1265 int w
= vsnprintf(buf
, buf_size
, fmt
, args
);
1269 if (w
== -1 || w
>= (int)buf_size
)
1270 w
= (int)buf_size
- 1;
1275 int ImFormatStringV(char* buf
, size_t buf_size
, const char* fmt
, va_list args
)
1277 IM_ASSERT(fmt
!= NULL
);
1278 int w
= vsnprintf(buf
, buf_size
, fmt
, args
);
1281 if (w
== -1 || w
>= (int)buf_size
)
1282 w
= (int)buf_size
- 1;
1286 #endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1288 // Pass data_size==0 for zero-terminated strings
1289 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
1290 ImU32
ImHash(const void* data
, int data_size
, ImU32 seed
)
1292 static ImU32 crc32_lut
[256] = { 0 };
1295 const ImU32 polynomial
= 0xEDB88320;
1296 for (ImU32 i
= 0; i
< 256; i
++)
1299 for (ImU32 j
= 0; j
< 8; j
++)
1300 crc
= (crc
>> 1) ^ (ImU32(-int(crc
& 1)) & polynomial
);
1307 const unsigned char* current
= (const unsigned char*)data
;
1313 crc
= (crc
>> 8) ^ crc32_lut
[(crc
& 0xFF) ^ *current
++];
1317 // Zero-terminated string
1318 while (unsigned char c
= *current
++)
1320 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1321 // Because this syntax is rarely used we are optimizing for the common case.
1322 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1323 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
1324 if (c
== '#' && current
[0] == '#' && current
[1] == '#')
1326 crc
= (crc
>> 8) ^ crc32_lut
[(crc
& 0xFF) ^ c
];
1332 //-----------------------------------------------------------------------------
1334 //-----------------------------------------------------------------------------
1336 // Convert UTF-8 to 32-bits character, process single character input.
1337 // Based on stb_from_utf8() from github.com/nothings/stb/
1338 // We handle UTF-8 decoding error by skipping forward.
1339 int ImTextCharFromUtf8(unsigned int* out_char
, const char* in_text
, const char* in_text_end
)
1341 unsigned int c
= (unsigned int)-1;
1342 const unsigned char* str
= (const unsigned char*)in_text
;
1345 c
= (unsigned int)(*str
++);
1349 if ((*str
& 0xe0) == 0xc0)
1351 *out_char
= 0xFFFD; // will be invalid but not end of string
1352 if (in_text_end
&& in_text_end
- (const char*)str
< 2) return 1;
1353 if (*str
< 0xc2) return 2;
1354 c
= (unsigned int)((*str
++ & 0x1f) << 6);
1355 if ((*str
& 0xc0) != 0x80) return 2;
1356 c
+= (*str
++ & 0x3f);
1360 if ((*str
& 0xf0) == 0xe0)
1362 *out_char
= 0xFFFD; // will be invalid but not end of string
1363 if (in_text_end
&& in_text_end
- (const char*)str
< 3) return 1;
1364 if (*str
== 0xe0 && (str
[1] < 0xa0 || str
[1] > 0xbf)) return 3;
1365 if (*str
== 0xed && str
[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1366 c
= (unsigned int)((*str
++ & 0x0f) << 12);
1367 if ((*str
& 0xc0) != 0x80) return 3;
1368 c
+= (unsigned int)((*str
++ & 0x3f) << 6);
1369 if ((*str
& 0xc0) != 0x80) return 3;
1370 c
+= (*str
++ & 0x3f);
1374 if ((*str
& 0xf8) == 0xf0)
1376 *out_char
= 0xFFFD; // will be invalid but not end of string
1377 if (in_text_end
&& in_text_end
- (const char*)str
< 4) return 1;
1378 if (*str
> 0xf4) return 4;
1379 if (*str
== 0xf0 && (str
[1] < 0x90 || str
[1] > 0xbf)) return 4;
1380 if (*str
== 0xf4 && str
[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1381 c
= (unsigned int)((*str
++ & 0x07) << 18);
1382 if ((*str
& 0xc0) != 0x80) return 4;
1383 c
+= (unsigned int)((*str
++ & 0x3f) << 12);
1384 if ((*str
& 0xc0) != 0x80) return 4;
1385 c
+= (unsigned int)((*str
++ & 0x3f) << 6);
1386 if ((*str
& 0xc0) != 0x80) return 4;
1387 c
+= (*str
++ & 0x3f);
1388 // utf-8 encodings of values used in surrogate pairs are invalid
1389 if ((c
& 0xFFFFF800) == 0xD800) return 4;
1397 int ImTextStrFromUtf8(ImWchar
* buf
, int buf_size
, const char* in_text
, const char* in_text_end
, const char** in_text_remaining
)
1399 ImWchar
* buf_out
= buf
;
1400 ImWchar
* buf_end
= buf
+ buf_size
;
1401 while (buf_out
< buf_end
-1 && (!in_text_end
|| in_text
< in_text_end
) && *in_text
)
1404 in_text
+= ImTextCharFromUtf8(&c
, in_text
, in_text_end
);
1407 if (c
< 0x10000) // FIXME: Losing characters that don't fit in 2 bytes
1408 *buf_out
++ = (ImWchar
)c
;
1411 if (in_text_remaining
)
1412 *in_text_remaining
= in_text
;
1413 return (int)(buf_out
- buf
);
1416 int ImTextCountCharsFromUtf8(const char* in_text
, const char* in_text_end
)
1419 while ((!in_text_end
|| in_text
< in_text_end
) && *in_text
)
1422 in_text
+= ImTextCharFromUtf8(&c
, in_text
, in_text_end
);
1431 // Based on stb_to_utf8() from github.com/nothings/stb/
1432 static inline int ImTextCharToUtf8(char* buf
, int buf_size
, unsigned int c
)
1441 if (buf_size
< 2) return 0;
1442 buf
[0] = (char)(0xc0 + (c
>> 6));
1443 buf
[1] = (char)(0x80 + (c
& 0x3f));
1446 if (c
>= 0xdc00 && c
< 0xe000)
1450 if (c
>= 0xd800 && c
< 0xdc00)
1452 if (buf_size
< 4) return 0;
1453 buf
[0] = (char)(0xf0 + (c
>> 18));
1454 buf
[1] = (char)(0x80 + ((c
>> 12) & 0x3f));
1455 buf
[2] = (char)(0x80 + ((c
>> 6) & 0x3f));
1456 buf
[3] = (char)(0x80 + ((c
) & 0x3f));
1459 //else if (c < 0x10000)
1461 if (buf_size
< 3) return 0;
1462 buf
[0] = (char)(0xe0 + (c
>> 12));
1463 buf
[1] = (char)(0x80 + ((c
>> 6) & 0x3f));
1464 buf
[2] = (char)(0x80 + ((c
) & 0x3f));
1469 static inline int ImTextCountUtf8BytesFromChar(unsigned int c
)
1471 if (c
< 0x80) return 1;
1472 if (c
< 0x800) return 2;
1473 if (c
>= 0xdc00 && c
< 0xe000) return 0;
1474 if (c
>= 0xd800 && c
< 0xdc00) return 4;
1478 int ImTextStrToUtf8(char* buf
, int buf_size
, const ImWchar
* in_text
, const ImWchar
* in_text_end
)
1480 char* buf_out
= buf
;
1481 const char* buf_end
= buf
+ buf_size
;
1482 while (buf_out
< buf_end
-1 && (!in_text_end
|| in_text
< in_text_end
) && *in_text
)
1484 unsigned int c
= (unsigned int)(*in_text
++);
1486 *buf_out
++ = (char)c
;
1488 buf_out
+= ImTextCharToUtf8(buf_out
, (int)(buf_end
-buf_out
-1), c
);
1491 return (int)(buf_out
- buf
);
1494 int ImTextCountUtf8BytesFromStr(const ImWchar
* in_text
, const ImWchar
* in_text_end
)
1496 int bytes_count
= 0;
1497 while ((!in_text_end
|| in_text
< in_text_end
) && *in_text
)
1499 unsigned int c
= (unsigned int)(*in_text
++);
1503 bytes_count
+= ImTextCountUtf8BytesFromChar(c
);
1508 ImVec4
ImGui::ColorConvertU32ToFloat4(ImU32 in
)
1510 float s
= 1.0f
/255.0f
;
1512 ((in
>> IM_COL32_R_SHIFT
) & 0xFF) * s
,
1513 ((in
>> IM_COL32_G_SHIFT
) & 0xFF) * s
,
1514 ((in
>> IM_COL32_B_SHIFT
) & 0xFF) * s
,
1515 ((in
>> IM_COL32_A_SHIFT
) & 0xFF) * s
);
1518 ImU32
ImGui::ColorConvertFloat4ToU32(const ImVec4
& in
)
1521 out
= ((ImU32
)IM_F32_TO_INT8_SAT(in
.x
)) << IM_COL32_R_SHIFT
;
1522 out
|= ((ImU32
)IM_F32_TO_INT8_SAT(in
.y
)) << IM_COL32_G_SHIFT
;
1523 out
|= ((ImU32
)IM_F32_TO_INT8_SAT(in
.z
)) << IM_COL32_B_SHIFT
;
1524 out
|= ((ImU32
)IM_F32_TO_INT8_SAT(in
.w
)) << IM_COL32_A_SHIFT
;
1528 ImU32
ImGui::GetColorU32(ImGuiCol idx
, float alpha_mul
)
1530 ImGuiStyle
& style
= GImGui
->Style
;
1531 ImVec4 c
= style
.Colors
[idx
];
1532 c
.w
*= style
.Alpha
* alpha_mul
;
1533 return ColorConvertFloat4ToU32(c
);
1536 ImU32
ImGui::GetColorU32(const ImVec4
& col
)
1538 ImGuiStyle
& style
= GImGui
->Style
;
1541 return ColorConvertFloat4ToU32(c
);
1544 const ImVec4
& ImGui::GetStyleColorVec4(ImGuiCol idx
)
1546 ImGuiStyle
& style
= GImGui
->Style
;
1547 return style
.Colors
[idx
];
1550 ImU32
ImGui::GetColorU32(ImU32 col
)
1552 float style_alpha
= GImGui
->Style
.Alpha
;
1553 if (style_alpha
>= 1.0f
)
1555 ImU32 a
= (col
& IM_COL32_A_MASK
) >> IM_COL32_A_SHIFT
;
1556 a
= (ImU32
)(a
* style_alpha
); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1557 return (col
& ~IM_COL32_A_MASK
) | (a
<< IM_COL32_A_SHIFT
);
1560 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1561 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
1562 void ImGui::ColorConvertRGBtoHSV(float r
, float g
, float b
, float& out_h
, float& out_s
, float& out_v
)
1576 const float chroma
= r
- (g
< b
? g
: b
);
1577 out_h
= ImFabs(K
+ (g
- b
) / (6.f
* chroma
+ 1e-20f
));
1578 out_s
= chroma
/ (r
+ 1e-20f
);
1582 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1583 // also http://en.wikipedia.org/wiki/HSL_and_HSV
1584 void ImGui::ColorConvertHSVtoRGB(float h
, float s
, float v
, float& out_r
, float& out_g
, float& out_b
)
1589 out_r
= out_g
= out_b
= v
;
1593 h
= ImFmod(h
, 1.0f
) / (60.0f
/360.0f
);
1595 float f
= h
- (float)i
;
1596 float p
= v
* (1.0f
- s
);
1597 float q
= v
* (1.0f
- s
* f
);
1598 float t
= v
* (1.0f
- s
* (1.0f
- f
));
1602 case 0: out_r
= v
; out_g
= t
; out_b
= p
; break;
1603 case 1: out_r
= q
; out_g
= v
; out_b
= p
; break;
1604 case 2: out_r
= p
; out_g
= v
; out_b
= t
; break;
1605 case 3: out_r
= p
; out_g
= q
; out_b
= v
; break;
1606 case 4: out_r
= t
; out_g
= p
; out_b
= v
; break;
1607 case 5: default: out_r
= v
; out_g
= p
; out_b
= q
; break;
1611 FILE* ImFileOpen(const char* filename
, const char* mode
)
1613 #if defined(_WIN32) && !defined(__CYGWIN__)
1614 // 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)
1615 const int filename_wsize
= ImTextCountCharsFromUtf8(filename
, NULL
) + 1;
1616 const int mode_wsize
= ImTextCountCharsFromUtf8(mode
, NULL
) + 1;
1617 ImVector
<ImWchar
> buf
;
1618 buf
.resize(filename_wsize
+ mode_wsize
);
1619 ImTextStrFromUtf8(&buf
[0], filename_wsize
, filename
, NULL
);
1620 ImTextStrFromUtf8(&buf
[filename_wsize
], mode_wsize
, mode
, NULL
);
1621 return _wfopen((wchar_t*)&buf
[0], (wchar_t*)&buf
[filename_wsize
]);
1623 return fopen(filename
, mode
);
1627 // Load file content into memory
1628 // Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree()
1629 void* ImFileLoadToMemory(const char* filename
, const char* file_open_mode
, size_t* out_file_size
, int padding_bytes
)
1631 IM_ASSERT(filename
&& file_open_mode
);
1636 if ((f
= ImFileOpen(filename
, file_open_mode
)) == NULL
)
1639 long file_size_signed
;
1640 if (fseek(f
, 0, SEEK_END
) || (file_size_signed
= ftell(f
)) == -1 || fseek(f
, 0, SEEK_SET
))
1646 size_t file_size
= (size_t)file_size_signed
;
1647 void* file_data
= ImGui::MemAlloc(file_size
+ padding_bytes
);
1648 if (file_data
== NULL
)
1653 if (fread(file_data
, 1, file_size
, f
) != file_size
)
1656 ImGui::MemFree(file_data
);
1659 if (padding_bytes
> 0)
1660 memset((void *)(((char*)file_data
) + file_size
), 0, (size_t)padding_bytes
);
1664 *out_file_size
= file_size
;
1669 //-----------------------------------------------------------------------------
1671 // Helper: Key->value storage
1672 //-----------------------------------------------------------------------------
1674 // std::lower_bound but without the bullshit
1675 static ImVector
<ImGuiStorage::Pair
>::iterator
LowerBound(ImVector
<ImGuiStorage::Pair
>& data
, ImGuiID key
)
1677 ImVector
<ImGuiStorage::Pair
>::iterator first
= data
.begin();
1678 ImVector
<ImGuiStorage::Pair
>::iterator last
= data
.end();
1679 size_t count
= (size_t)(last
- first
);
1682 size_t count2
= count
>> 1;
1683 ImVector
<ImGuiStorage::Pair
>::iterator mid
= first
+ count2
;
1687 count
-= count2
+ 1;
1697 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
1698 void ImGuiStorage::BuildSortByKey()
1702 static int IMGUI_CDECL
PairCompareByID(const void* lhs
, const void* rhs
)
1704 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1705 if (((const Pair
*)lhs
)->key
> ((const Pair
*)rhs
)->key
) return +1;
1706 if (((const Pair
*)lhs
)->key
< ((const Pair
*)rhs
)->key
) return -1;
1711 ImQsort(Data
.Data
, (size_t)Data
.Size
, sizeof(Pair
), StaticFunc::PairCompareByID
);
1714 int ImGuiStorage::GetInt(ImGuiID key
, int default_val
) const
1716 ImVector
<Pair
>::iterator it
= LowerBound(const_cast<ImVector
<ImGuiStorage::Pair
>&>(Data
), key
);
1717 if (it
== Data
.end() || it
->key
!= key
)
1722 bool ImGuiStorage::GetBool(ImGuiID key
, bool default_val
) const
1724 return GetInt(key
, default_val
? 1 : 0) != 0;
1727 float ImGuiStorage::GetFloat(ImGuiID key
, float default_val
) const
1729 ImVector
<Pair
>::iterator it
= LowerBound(const_cast<ImVector
<ImGuiStorage::Pair
>&>(Data
), key
);
1730 if (it
== Data
.end() || it
->key
!= key
)
1735 void* ImGuiStorage::GetVoidPtr(ImGuiID key
) const
1737 ImVector
<Pair
>::iterator it
= LowerBound(const_cast<ImVector
<ImGuiStorage::Pair
>&>(Data
), key
);
1738 if (it
== Data
.end() || it
->key
!= key
)
1743 // References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer.
1744 int* ImGuiStorage::GetIntRef(ImGuiID key
, int default_val
)
1746 ImVector
<Pair
>::iterator it
= LowerBound(Data
, key
);
1747 if (it
== Data
.end() || it
->key
!= key
)
1748 it
= Data
.insert(it
, Pair(key
, default_val
));
1752 bool* ImGuiStorage::GetBoolRef(ImGuiID key
, bool default_val
)
1754 return (bool*)GetIntRef(key
, default_val
? 1 : 0);
1757 float* ImGuiStorage::GetFloatRef(ImGuiID key
, float default_val
)
1759 ImVector
<Pair
>::iterator it
= LowerBound(Data
, key
);
1760 if (it
== Data
.end() || it
->key
!= key
)
1761 it
= Data
.insert(it
, Pair(key
, default_val
));
1765 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key
, void* default_val
)
1767 ImVector
<Pair
>::iterator it
= LowerBound(Data
, key
);
1768 if (it
== Data
.end() || it
->key
!= key
)
1769 it
= Data
.insert(it
, Pair(key
, default_val
));
1773 // FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame)
1774 void ImGuiStorage::SetInt(ImGuiID key
, int val
)
1776 ImVector
<Pair
>::iterator it
= LowerBound(Data
, key
);
1777 if (it
== Data
.end() || it
->key
!= key
)
1779 Data
.insert(it
, Pair(key
, val
));
1785 void ImGuiStorage::SetBool(ImGuiID key
, bool val
)
1787 SetInt(key
, val
? 1 : 0);
1790 void ImGuiStorage::SetFloat(ImGuiID key
, float val
)
1792 ImVector
<Pair
>::iterator it
= LowerBound(Data
, key
);
1793 if (it
== Data
.end() || it
->key
!= key
)
1795 Data
.insert(it
, Pair(key
, val
));
1801 void ImGuiStorage::SetVoidPtr(ImGuiID key
, void* val
)
1803 ImVector
<Pair
>::iterator it
= LowerBound(Data
, key
);
1804 if (it
== Data
.end() || it
->key
!= key
)
1806 Data
.insert(it
, Pair(key
, val
));
1812 void ImGuiStorage::SetAllInt(int v
)
1814 for (int i
= 0; i
< Data
.Size
; i
++)
1818 //-----------------------------------------------------------------------------
1820 //-----------------------------------------------------------------------------
1822 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
1823 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter
)
1827 ImStrncpy(InputBuf
, default_filter
, IM_ARRAYSIZE(InputBuf
));
1837 bool ImGuiTextFilter::Draw(const char* label
, float width
)
1840 ImGui::PushItemWidth(width
);
1841 bool value_changed
= ImGui::InputText(label
, InputBuf
, IM_ARRAYSIZE(InputBuf
));
1843 ImGui::PopItemWidth();
1846 return value_changed
;
1849 void ImGuiTextFilter::TextRange::split(char separator
, ImVector
<TextRange
>* out
) const
1853 const char* we
= wb
;
1856 if (*we
== separator
)
1858 out
->push_back(TextRange(wb
, we
));
1864 out
->push_back(TextRange(wb
, we
));
1867 void ImGuiTextFilter::Build()
1870 TextRange
input_range(InputBuf
, InputBuf
+strlen(InputBuf
));
1871 input_range
.split(',', &Filters
);
1874 for (int i
= 0; i
!= Filters
.Size
; i
++)
1876 TextRange
& f
= Filters
[i
];
1877 while (f
.b
< f
.e
&& ImCharIsBlankA(f
.b
[0]))
1879 while (f
.e
> f
.b
&& ImCharIsBlankA(f
.e
[-1]))
1883 if (Filters
[i
].b
[0] != '-')
1888 bool ImGuiTextFilter::PassFilter(const char* text
, const char* text_end
) const
1890 if (Filters
.empty())
1896 for (int i
= 0; i
!= Filters
.Size
; i
++)
1898 const TextRange
& f
= Filters
[i
];
1904 if (ImStristr(text
, text_end
, f
.begin()+1, f
.end()) != NULL
)
1910 if (ImStristr(text
, text_end
, f
.begin(), f
.end()) != NULL
)
1922 //-----------------------------------------------------------------------------
1924 //-----------------------------------------------------------------------------
1926 // On some platform vsnprintf() takes va_list by reference and modifies it.
1927 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1929 #define va_copy(dest, src) (dest = src)
1932 // Helper: Text buffer for logging/accumulating text
1933 void ImGuiTextBuffer::appendfv(const char* fmt
, va_list args
)
1936 va_copy(args_copy
, args
);
1938 int len
= ImFormatStringV(NULL
, 0, fmt
, args
); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
1945 const int write_off
= Buf
.Size
;
1946 const int needed_sz
= write_off
+ len
;
1947 if (write_off
+ len
>= Buf
.Capacity
)
1949 int double_capacity
= Buf
.Capacity
* 2;
1950 Buf
.reserve(needed_sz
> double_capacity
? needed_sz
: double_capacity
);
1953 Buf
.resize(needed_sz
);
1954 ImFormatStringV(&Buf
[write_off
- 1], (size_t)len
+ 1, fmt
, args_copy
);
1958 void ImGuiTextBuffer::appendf(const char* fmt
, ...)
1961 va_start(args
, fmt
);
1962 appendfv(fmt
, args
);
1966 //-----------------------------------------------------------------------------
1967 // ImGuiSimpleColumns (internal use only)
1968 //-----------------------------------------------------------------------------
1970 ImGuiMenuColumns::ImGuiMenuColumns()
1973 Spacing
= Width
= NextWidth
= 0.0f
;
1974 memset(Pos
, 0, sizeof(Pos
));
1975 memset(NextWidths
, 0, sizeof(NextWidths
));
1978 void ImGuiMenuColumns::Update(int count
, float spacing
, bool clear
)
1980 IM_ASSERT(Count
<= IM_ARRAYSIZE(Pos
));
1982 Width
= NextWidth
= 0.0f
;
1984 if (clear
) memset(NextWidths
, 0, sizeof(NextWidths
));
1985 for (int i
= 0; i
< Count
; i
++)
1987 if (i
> 0 && NextWidths
[i
] > 0.0f
)
1989 Pos
[i
] = (float)(int)Width
;
1990 Width
+= NextWidths
[i
];
1991 NextWidths
[i
] = 0.0f
;
1995 float ImGuiMenuColumns::DeclColumns(float w0
, float w1
, float w2
) // not using va_arg because they promote float to double
1998 NextWidths
[0] = ImMax(NextWidths
[0], w0
);
1999 NextWidths
[1] = ImMax(NextWidths
[1], w1
);
2000 NextWidths
[2] = ImMax(NextWidths
[2], w2
);
2001 for (int i
= 0; i
< 3; i
++)
2002 NextWidth
+= NextWidths
[i
] + ((i
> 0 && NextWidths
[i
] > 0.0f
) ? Spacing
: 0.0f
);
2003 return ImMax(Width
, NextWidth
);
2006 float ImGuiMenuColumns::CalcExtraSpace(float avail_w
)
2008 return ImMax(0.0f
, avail_w
- Width
);
2011 //-----------------------------------------------------------------------------
2013 //-----------------------------------------------------------------------------
2015 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y
, float line_height
)
2017 // Set cursor position and a few other things so that SetScrollHere() and Columns() can work when seeking cursor.
2018 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2019 // The clipper should probably have a 4th step to display the last item in a regular manner.
2020 ImGui::SetCursorPosY(pos_y
);
2021 ImGuiWindow
* window
= ImGui::GetCurrentWindow();
2022 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.
2023 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.
2024 if (window
->DC
.ColumnsSet
)
2025 window
->DC
.ColumnsSet
->LineMinY
= window
->DC
.CursorPos
.y
; // Setting this so that cell Y position are set properly
2028 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2029 // Use case B: Begin() called from constructor with items_height>0
2030 // 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.
2031 void ImGuiListClipper::Begin(int count
, float items_height
)
2033 StartPosY
= ImGui::GetCursorPosY();
2034 ItemsHeight
= items_height
;
2037 DisplayEnd
= DisplayStart
= -1;
2038 if (ItemsHeight
> 0.0f
)
2040 ImGui::CalcListClipping(ItemsCount
, ItemsHeight
, &DisplayStart
, &DisplayEnd
); // calculate how many to clip/display
2041 if (DisplayStart
> 0)
2042 SetCursorPosYAndSetupDummyPrevLine(StartPosY
+ DisplayStart
* ItemsHeight
, ItemsHeight
); // advance cursor
2047 void ImGuiListClipper::End()
2051 // 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.
2052 if (ItemsCount
< INT_MAX
)
2053 SetCursorPosYAndSetupDummyPrevLine(StartPosY
+ ItemsCount
* ItemsHeight
, ItemsHeight
); // advance cursor
2058 bool ImGuiListClipper::Step()
2060 if (ItemsCount
== 0 || ImGui::GetCurrentWindowRead()->SkipItems
)
2065 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.
2069 StartPosY
= ImGui::GetCursorPosY();
2073 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.
2075 if (ItemsCount
== 1) { ItemsCount
= -1; return false; }
2076 float items_height
= ImGui::GetCursorPosY() - StartPosY
;
2077 IM_ASSERT(items_height
> 0.0f
); // If this triggers, it means Item 0 hasn't moved the cursor vertically
2078 Begin(ItemsCount
-1, items_height
);
2084 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.
2086 IM_ASSERT(DisplayStart
>= 0 && DisplayEnd
>= 0);
2090 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.
2095 //-----------------------------------------------------------------------------
2097 //-----------------------------------------------------------------------------
2099 ImGuiWindow::ImGuiWindow(ImGuiContext
* context
, const char* name
)
2100 : DrawListInst(&context
->DrawListSharedData
)
2102 Name
= ImStrdup(name
);
2103 ID
= ImHash(name
, 0);
2104 IDStack
.push_back(ID
);
2106 Pos
= ImVec2(0.0f
, 0.0f
);
2107 Size
= SizeFull
= ImVec2(0.0f
, 0.0f
);
2108 SizeContents
= SizeContentsExplicit
= ImVec2(0.0f
, 0.0f
);
2109 WindowPadding
= ImVec2(0.0f
, 0.0f
);
2110 WindowRounding
= 0.0f
;
2111 WindowBorderSize
= 0.0f
;
2112 MoveId
= GetID("#MOVE");
2114 Scroll
= ImVec2(0.0f
, 0.0f
);
2115 ScrollTarget
= ImVec2(FLT_MAX
, FLT_MAX
);
2116 ScrollTargetCenterRatio
= ImVec2(0.5f
, 0.5f
);
2117 ScrollbarSizes
= ImVec2(0.0f
, 0.0f
);
2118 ScrollbarX
= ScrollbarY
= false;
2119 Active
= WasActive
= false;
2120 WriteAccessed
= false;
2122 CollapseToggleWanted
= false;
2126 HasCloseButton
= false;
2127 BeginOrderWithinParent
= -1;
2128 BeginOrderWithinContext
= -1;
2131 AutoFitFramesX
= AutoFitFramesY
= -1;
2132 AutoFitOnlyGrows
= false;
2133 AutoFitChildAxises
= 0x00;
2134 AutoPosLastDirection
= ImGuiDir_None
;
2135 HiddenFramesRegular
= HiddenFramesForResize
= 0;
2136 SetWindowPosAllowFlags
= SetWindowSizeAllowFlags
= SetWindowCollapsedAllowFlags
= ImGuiCond_Always
| ImGuiCond_Once
| ImGuiCond_FirstUseEver
| ImGuiCond_Appearing
;
2137 SetWindowPosVal
= SetWindowPosPivot
= ImVec2(FLT_MAX
, FLT_MAX
);
2139 LastFrameActive
= -1;
2140 ItemWidthDefault
= 0.0f
;
2141 FontWindowScale
= 1.0f
;
2144 DrawList
= &DrawListInst
;
2145 DrawList
->_OwnerName
= Name
;
2146 ParentWindow
= NULL
;
2148 RootWindowForTitleBarHighlight
= NULL
;
2149 RootWindowForNav
= NULL
;
2151 NavLastIds
[0] = NavLastIds
[1] = 0;
2152 NavRectRel
[0] = NavRectRel
[1] = ImRect();
2153 NavLastChildNavWindow
= NULL
;
2155 FocusIdxAllCounter
= FocusIdxTabCounter
= -1;
2156 FocusIdxAllRequestCurrent
= FocusIdxTabRequestCurrent
= INT_MAX
;
2157 FocusIdxAllRequestNext
= FocusIdxTabRequestNext
= INT_MAX
;
2160 ImGuiWindow::~ImGuiWindow()
2162 IM_ASSERT(DrawList
== &DrawListInst
);
2164 for (int i
= 0; i
!= ColumnsStorage
.Size
; i
++)
2165 ColumnsStorage
[i
].~ImGuiColumnsSet();
2168 ImGuiID
ImGuiWindow::GetID(const char* str
, const char* str_end
)
2170 ImGuiID seed
= IDStack
.back();
2171 ImGuiID id
= ImHash(str
, str_end
? (int)(str_end
- str
) : 0, seed
);
2172 ImGui::KeepAliveID(id
);
2176 ImGuiID
ImGuiWindow::GetID(const void* ptr
)
2178 ImGuiID seed
= IDStack
.back();
2179 ImGuiID id
= ImHash(&ptr
, sizeof(void*), seed
);
2180 ImGui::KeepAliveID(id
);
2184 ImGuiID
ImGuiWindow::GetIDNoKeepAlive(const char* str
, const char* str_end
)
2186 ImGuiID seed
= IDStack
.back();
2187 return ImHash(str
, str_end
? (int)(str_end
- str
) : 0, seed
);
2190 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
2191 ImGuiID
ImGuiWindow::GetIDFromRectangle(const ImRect
& r_abs
)
2193 ImGuiID seed
= IDStack
.back();
2194 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
) };
2195 ImGuiID id
= ImHash(&r_rel
, sizeof(r_rel
), seed
);
2196 ImGui::KeepAliveID(id
);
2200 //-----------------------------------------------------------------------------
2201 // Internal API exposed in imgui_internal.h
2202 //-----------------------------------------------------------------------------
2204 static void SetCurrentWindow(ImGuiWindow
* window
)
2206 ImGuiContext
& g
= *GImGui
;
2207 g
.CurrentWindow
= window
;
2209 g
.FontSize
= g
.DrawListSharedData
.FontSize
= window
->CalcFontSize();
2212 static void SetNavID(ImGuiID id
, int nav_layer
)
2214 ImGuiContext
& g
= *GImGui
;
2215 IM_ASSERT(g
.NavWindow
);
2216 IM_ASSERT(nav_layer
== 0 || nav_layer
== 1);
2218 g
.NavWindow
->NavLastIds
[nav_layer
] = id
;
2221 static void SetNavIDWithRectRel(ImGuiID id
, int nav_layer
, const ImRect
& rect_rel
)
2223 ImGuiContext
& g
= *GImGui
;
2224 SetNavID(id
, nav_layer
);
2225 g
.NavWindow
->NavRectRel
[nav_layer
] = rect_rel
;
2226 g
.NavMousePosDirty
= true;
2227 g
.NavDisableHighlight
= false;
2228 g
.NavDisableMouseHover
= true;
2231 void ImGui::SetActiveID(ImGuiID id
, ImGuiWindow
* window
)
2233 ImGuiContext
& g
= *GImGui
;
2234 g
.ActiveIdIsJustActivated
= (g
.ActiveId
!= id
);
2235 if (g
.ActiveIdIsJustActivated
)
2237 g
.ActiveIdTimer
= 0.0f
;
2238 g
.ActiveIdValueChanged
= false;
2241 g
.LastActiveId
= id
;
2242 g
.LastActiveIdTimer
= 0.0f
;
2246 g
.ActiveIdAllowNavDirFlags
= 0;
2247 g
.ActiveIdAllowOverlap
= false;
2248 g
.ActiveIdWindow
= window
;
2251 g
.ActiveIdIsAlive
= true;
2252 g
.ActiveIdSource
= (g
.NavActivateId
== id
|| g
.NavInputId
== id
|| g
.NavJustTabbedId
== id
|| g
.NavJustMovedToId
== id
) ? ImGuiInputSource_Nav
: ImGuiInputSource_Mouse
;
2256 void ImGui::SetFocusID(ImGuiID id
, ImGuiWindow
* window
)
2258 ImGuiContext
& g
= *GImGui
;
2261 // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it.
2262 const int nav_layer
= window
->DC
.NavLayerCurrent
;
2263 if (g
.NavWindow
!= window
)
2264 g
.NavInitRequest
= false;
2266 g
.NavWindow
= window
;
2267 g
.NavLayer
= nav_layer
;
2268 window
->NavLastIds
[nav_layer
] = id
;
2269 if (window
->DC
.LastItemId
== id
)
2270 window
->NavRectRel
[nav_layer
] = ImRect(window
->DC
.LastItemRect
.Min
- window
->Pos
, window
->DC
.LastItemRect
.Max
- window
->Pos
);
2272 if (g
.ActiveIdSource
== ImGuiInputSource_Nav
)
2273 g
.NavDisableMouseHover
= true;
2275 g
.NavDisableHighlight
= true;
2278 void ImGui::ClearActiveID()
2280 SetActiveID(0, NULL
);
2283 void ImGui::SetHoveredID(ImGuiID id
)
2285 ImGuiContext
& g
= *GImGui
;
2287 g
.HoveredIdAllowOverlap
= false;
2288 g
.HoveredIdTimer
= (id
!= 0 && g
.HoveredIdPreviousFrame
== id
) ? (g
.HoveredIdTimer
+ g
.IO
.DeltaTime
) : 0.0f
;
2291 ImGuiID
ImGui::GetHoveredID()
2293 ImGuiContext
& g
= *GImGui
;
2294 return g
.HoveredId
? g
.HoveredId
: g
.HoveredIdPreviousFrame
;
2297 void ImGui::KeepAliveID(ImGuiID id
)
2299 ImGuiContext
& g
= *GImGui
;
2300 if (g
.ActiveId
== id
)
2301 g
.ActiveIdIsAlive
= true;
2302 if (g
.ActiveIdPreviousFrame
== id
)
2303 g
.ActiveIdPreviousFrameIsAlive
= true;
2306 void ImGui::MarkItemValueChanged(ImGuiID id
)
2308 // This marking is solely to be able to provide info for IsItemDeactivatedAfterChange().
2309 // 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.
2310 (void)id
; // Avoid unused variable warnings when asserts are compiled out.
2311 ImGuiContext
& g
= *GImGui
;
2312 IM_ASSERT(g
.ActiveId
== id
|| g
.ActiveId
== 0 || g
.DragDropActive
);
2313 g
.ActiveIdValueChanged
= true;
2316 static inline bool IsWindowContentHoverable(ImGuiWindow
* window
, ImGuiHoveredFlags flags
)
2318 // An active popup disable hovering on other windows (apart from its own children)
2319 // FIXME-OPT: This could be cached/stored within the window.
2320 ImGuiContext
& g
= *GImGui
;
2322 if (ImGuiWindow
* focused_root_window
= g
.NavWindow
->RootWindow
)
2323 if (focused_root_window
->WasActive
&& focused_root_window
!= window
->RootWindow
)
2325 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
2326 // NB: The order of those two tests is important because Modal windows are also Popups.
2327 if (focused_root_window
->Flags
& ImGuiWindowFlags_Modal
)
2329 if ((focused_root_window
->Flags
& ImGuiWindowFlags_Popup
) && !(flags
& ImGuiHoveredFlags_AllowWhenBlockedByPopup
))
2336 // Advance cursor given item size for layout.
2337 void ImGui::ItemSize(const ImVec2
& size
, float text_offset_y
)
2339 ImGuiContext
& g
= *GImGui
;
2340 ImGuiWindow
* window
= g
.CurrentWindow
;
2341 if (window
->SkipItems
)
2344 // Always align ourselves on pixel boundaries
2345 const float line_height
= ImMax(window
->DC
.CurrentLineHeight
, size
.y
);
2346 const float text_base_offset
= ImMax(window
->DC
.CurrentLineTextBaseOffset
, text_offset_y
);
2347 //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]
2348 window
->DC
.CursorPosPrevLine
= ImVec2(window
->DC
.CursorPos
.x
+ size
.x
, window
->DC
.CursorPos
.y
);
2349 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
));
2350 window
->DC
.CursorMaxPos
.x
= ImMax(window
->DC
.CursorMaxPos
.x
, window
->DC
.CursorPosPrevLine
.x
);
2351 window
->DC
.CursorMaxPos
.y
= ImMax(window
->DC
.CursorMaxPos
.y
, window
->DC
.CursorPos
.y
- g
.Style
.ItemSpacing
.y
);
2352 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2354 window
->DC
.PrevLineHeight
= line_height
;
2355 window
->DC
.PrevLineTextBaseOffset
= text_base_offset
;
2356 window
->DC
.CurrentLineHeight
= window
->DC
.CurrentLineTextBaseOffset
= 0.0f
;
2358 // Horizontal layout mode
2359 if (window
->DC
.LayoutType
== ImGuiLayoutType_Horizontal
)
2363 void ImGui::ItemSize(const ImRect
& bb
, float text_offset_y
)
2365 ItemSize(bb
.GetSize(), text_offset_y
);
2368 static ImGuiDir
inline NavScoreItemGetQuadrant(float dx
, float dy
)
2370 if (ImFabs(dx
) > ImFabs(dy
))
2371 return (dx
> 0.0f
) ? ImGuiDir_Right
: ImGuiDir_Left
;
2372 return (dy
> 0.0f
) ? ImGuiDir_Down
: ImGuiDir_Up
;
2375 static float inline NavScoreItemDistInterval(float a0
, float a1
, float b0
, float b1
)
2384 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir
, ImRect
& r
, const ImRect
& clip_rect
)
2386 if (move_dir
== ImGuiDir_Left
|| move_dir
== ImGuiDir_Right
)
2388 r
.Min
.y
= ImClamp(r
.Min
.y
, clip_rect
.Min
.y
, clip_rect
.Max
.y
);
2389 r
.Max
.y
= ImClamp(r
.Max
.y
, clip_rect
.Min
.y
, clip_rect
.Max
.y
);
2393 r
.Min
.x
= ImClamp(r
.Min
.x
, clip_rect
.Min
.x
, clip_rect
.Max
.x
);
2394 r
.Max
.x
= ImClamp(r
.Max
.x
, clip_rect
.Min
.x
, clip_rect
.Max
.x
);
2398 // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
2399 static bool NavScoreItem(ImGuiNavMoveResult
* result
, ImRect cand
)
2401 ImGuiContext
& g
= *GImGui
;
2402 ImGuiWindow
* window
= g
.CurrentWindow
;
2403 if (g
.NavLayer
!= window
->DC
.NavLayerCurrent
)
2406 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)
2407 g
.NavScoringCount
++;
2409 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
2410 if (window
->ParentWindow
== g
.NavWindow
)
2412 IM_ASSERT((window
->Flags
| g
.NavWindow
->Flags
) & ImGuiWindowFlags_NavFlattened
);
2413 if (!window
->ClipRect
.Contains(cand
))
2415 cand
.ClipWithFull(window
->ClipRect
); // This allows the scored item to not overlap other candidates in the parent window
2418 // 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)
2419 // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
2420 NavClampRectToVisibleAreaForMoveDir(g
.NavMoveClipDir
, cand
, window
->ClipRect
);
2422 // Compute distance between boxes
2423 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
2424 float dbx
= NavScoreItemDistInterval(cand
.Min
.x
, cand
.Max
.x
, curr
.Min
.x
, curr
.Max
.x
);
2425 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
2426 if (dby
!= 0.0f
&& dbx
!= 0.0f
)
2427 dbx
= (dbx
/1000.0f
) + ((dbx
> 0.0f
) ? +1.0f
: -1.0f
);
2428 float dist_box
= ImFabs(dbx
) + ImFabs(dby
);
2430 // 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)
2431 float dcx
= (cand
.Min
.x
+ cand
.Max
.x
) - (curr
.Min
.x
+ curr
.Max
.x
);
2432 float dcy
= (cand
.Min
.y
+ cand
.Max
.y
) - (curr
.Min
.y
+ curr
.Max
.y
);
2433 float dist_center
= ImFabs(dcx
) + ImFabs(dcy
); // L1 metric (need this for our connectedness guarantee)
2435 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
2437 float dax
= 0.0f
, day
= 0.0f
, dist_axial
= 0.0f
;
2438 if (dbx
!= 0.0f
|| dby
!= 0.0f
)
2440 // For non-overlapping boxes, use distance between boxes
2443 dist_axial
= dist_box
;
2444 quadrant
= NavScoreItemGetQuadrant(dbx
, dby
);
2446 else if (dcx
!= 0.0f
|| dcy
!= 0.0f
)
2448 // For overlapping boxes with different centers, use distance between centers
2451 dist_axial
= dist_center
;
2452 quadrant
= NavScoreItemGetQuadrant(dcx
, dcy
);
2456 // 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)
2457 quadrant
= (window
->DC
.LastItemId
< g
.NavId
) ? ImGuiDir_Left
: ImGuiDir_Right
;
2460 #if IMGUI_DEBUG_NAV_SCORING
2462 if (ImGui::IsMouseHoveringRect(cand
.Min
, cand
.Max
))
2464 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
]);
2465 ImDrawList
* draw_list
= ImGui::GetOverlayDrawList();
2466 draw_list
->AddRect(curr
.Min
, curr
.Max
, IM_COL32(255,200,0,100));
2467 draw_list
->AddRect(cand
.Min
, cand
.Max
, IM_COL32(255,255,0,200));
2468 draw_list
->AddRectFilled(cand
.Max
-ImVec2(4,4), cand
.Max
+ImGui::CalcTextSize(buf
)+ImVec2(4,4), IM_COL32(40,0,0,150));
2469 draw_list
->AddText(g
.IO
.FontDefault
, 13.0f
, cand
.Max
, ~0U, buf
);
2471 else if (g
.IO
.KeyCtrl
) // Hold to preview score in matching quadrant. Press C to rotate.
2473 if (IsKeyPressedMap(ImGuiKey_C
)) { g
.NavMoveDirLast
= (ImGuiDir
)((g
.NavMoveDirLast
+ 1) & 3); g
.IO
.KeysDownDuration
[g
.IO
.KeyMap
[ImGuiKey_C
]] = 0.01f
; }
2474 if (quadrant
== g
.NavMoveDir
)
2476 ImFormatString(buf
, IM_ARRAYSIZE(buf
), "%.0f/%.0f", dist_box
, dist_center
);
2477 ImDrawList
* draw_list
= ImGui::GetOverlayDrawList();
2478 draw_list
->AddRectFilled(cand
.Min
, cand
.Max
, IM_COL32(255, 0, 0, 200));
2479 draw_list
->AddText(g
.IO
.FontDefault
, 13.0f
, cand
.Min
, IM_COL32(255, 255, 255, 255), buf
);
2484 // Is it in the quadrant we're interesting in moving to?
2485 bool new_best
= false;
2486 if (quadrant
== g
.NavMoveDir
)
2488 // Does it beat the current best candidate?
2489 if (dist_box
< result
->DistBox
)
2491 result
->DistBox
= dist_box
;
2492 result
->DistCenter
= dist_center
;
2495 if (dist_box
== result
->DistBox
)
2497 // Try using distance between center points to break ties
2498 if (dist_center
< result
->DistCenter
)
2500 result
->DistCenter
= dist_center
;
2503 else if (dist_center
== result
->DistCenter
)
2505 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
2506 // (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),
2507 // 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.
2508 if (((g
.NavMoveDir
== ImGuiDir_Up
|| g
.NavMoveDir
== ImGuiDir_Down
) ? dby
: dbx
) < 0.0f
) // moving bj to the right/down decreases distance
2514 // 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
2515 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
2516 // 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.
2517 // 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.
2518 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
2519 if (result
->DistBox
== FLT_MAX
&& dist_axial
< result
->DistAxial
) // Check axial match
2520 if (g
.NavLayer
== 1 && !(g
.NavWindow
->Flags
& ImGuiWindowFlags_ChildMenu
))
2521 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
))
2523 result
->DistAxial
= dist_axial
;
2530 static void NavSaveLastChildNavWindow(ImGuiWindow
* child_window
)
2532 ImGuiWindow
* parent_window
= child_window
;
2533 while (parent_window
&& (parent_window
->Flags
& ImGuiWindowFlags_ChildWindow
) != 0 && (parent_window
->Flags
& (ImGuiWindowFlags_Popup
| ImGuiWindowFlags_ChildMenu
)) == 0)
2534 parent_window
= parent_window
->ParentWindow
;
2535 if (parent_window
&& parent_window
!= child_window
)
2536 parent_window
->NavLastChildNavWindow
= child_window
;
2539 // Call when we are expected to land on Layer 0 after FocusWindow()
2540 static ImGuiWindow
* NavRestoreLastChildNavWindow(ImGuiWindow
* window
)
2542 return window
->NavLastChildNavWindow
? window
->NavLastChildNavWindow
: window
;
2545 static void NavRestoreLayer(int layer
)
2547 ImGuiContext
& g
= *GImGui
;
2550 g
.NavWindow
= NavRestoreLastChildNavWindow(g
.NavWindow
);
2551 if (layer
== 0 && g
.NavWindow
->NavLastIds
[0] != 0)
2552 SetNavIDWithRectRel(g
.NavWindow
->NavLastIds
[0], layer
, g
.NavWindow
->NavRectRel
[0]);
2554 ImGui::NavInitWindow(g
.NavWindow
, true);
2557 static inline void NavUpdateAnyRequestFlag()
2559 ImGuiContext
& g
= *GImGui
;
2560 g
.NavAnyRequest
= g
.NavMoveRequest
|| g
.NavInitRequest
|| (IMGUI_DEBUG_NAV_SCORING
&& g
.NavWindow
!= NULL
);
2561 if (g
.NavAnyRequest
)
2562 IM_ASSERT(g
.NavWindow
!= NULL
);
2565 static bool NavMoveRequestButNoResultYet()
2567 ImGuiContext
& g
= *GImGui
;
2568 return g
.NavMoveRequest
&& g
.NavMoveResultLocal
.ID
== 0 && g
.NavMoveResultOther
.ID
== 0;
2571 void ImGui::NavMoveRequestCancel()
2573 ImGuiContext
& g
= *GImGui
;
2574 g
.NavMoveRequest
= false;
2575 NavUpdateAnyRequestFlag();
2578 // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
2579 static void ImGui::NavProcessItem(ImGuiWindow
* window
, const ImRect
& nav_bb
, const ImGuiID id
)
2581 ImGuiContext
& g
= *GImGui
;
2582 //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.
2585 const ImGuiItemFlags item_flags
= window
->DC
.ItemFlags
;
2586 const ImRect
nav_bb_rel(nav_bb
.Min
- window
->Pos
, nav_bb
.Max
- window
->Pos
);
2588 // Process Init Request
2589 if (g
.NavInitRequest
&& g
.NavLayer
== window
->DC
.NavLayerCurrent
)
2591 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
2592 if (!(item_flags
& ImGuiItemFlags_NoNavDefaultFocus
) || g
.NavInitResultId
== 0)
2594 g
.NavInitResultId
= id
;
2595 g
.NavInitResultRectRel
= nav_bb_rel
;
2597 if (!(item_flags
& ImGuiItemFlags_NoNavDefaultFocus
))
2599 g
.NavInitRequest
= false; // Found a match, clear request
2600 NavUpdateAnyRequestFlag();
2604 // Process Move Request (scoring for navigation)
2605 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
2606 if ((g
.NavId
!= id
|| (g
.NavMoveRequestFlags
& ImGuiNavMoveFlags_AllowCurrentNavId
)) && !(item_flags
& ImGuiItemFlags_NoNav
))
2608 ImGuiNavMoveResult
* result
= (window
== g
.NavWindow
) ? &g
.NavMoveResultLocal
: &g
.NavMoveResultOther
;
2609 #if IMGUI_DEBUG_NAV_SCORING
2610 // [DEBUG] Score all items in NavWindow at all times
2611 if (!g
.NavMoveRequest
)
2612 g
.NavMoveDir
= g
.NavMoveDirLast
;
2613 bool new_best
= NavScoreItem(result
, nav_bb
) && g
.NavMoveRequest
;
2615 bool new_best
= g
.NavMoveRequest
&& NavScoreItem(result
, nav_bb
);
2620 result
->Window
= window
;
2621 result
->RectRel
= nav_bb_rel
;
2624 const float VISIBLE_RATIO
= 0.70f
;
2625 if ((g
.NavMoveRequestFlags
& ImGuiNavMoveFlags_AlsoScoreVisibleSet
) && window
->ClipRect
.Overlaps(nav_bb
))
2626 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
)
2627 if (NavScoreItem(&g
.NavMoveResultLocalVisibleSet
, nav_bb
))
2629 result
= &g
.NavMoveResultLocalVisibleSet
;
2631 result
->Window
= window
;
2632 result
->RectRel
= nav_bb_rel
;
2636 // Update window-relative bounding box of navigated item
2639 g
.NavWindow
= window
; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
2640 g
.NavLayer
= window
->DC
.NavLayerCurrent
;
2641 g
.NavIdIsAlive
= true;
2642 g
.NavIdTabCounter
= window
->FocusIdxTabCounter
;
2643 window
->NavRectRel
[window
->DC
.NavLayerCurrent
] = nav_bb_rel
; // Store item bounding box (relative to window position)
2647 // Declare item bounding box for clipping and interaction.
2648 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2649 // declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
2650 bool ImGui::ItemAdd(const ImRect
& bb
, ImGuiID id
, const ImRect
* nav_bb_arg
)
2652 ImGuiContext
& g
= *GImGui
;
2653 ImGuiWindow
* window
= g
.CurrentWindow
;
2657 // Navigation processing runs prior to clipping early-out
2658 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2659 // (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.
2660 // 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.
2661 // 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)
2662 window
->DC
.NavLayerActiveMaskNext
|= window
->DC
.NavLayerCurrentMask
;
2663 if (g
.NavId
== id
|| g
.NavAnyRequest
)
2664 if (g
.NavWindow
->RootWindowForNav
== window
->RootWindowForNav
)
2665 if (window
== g
.NavWindow
|| ((window
->Flags
| g
.NavWindow
->Flags
) & ImGuiWindowFlags_NavFlattened
))
2666 NavProcessItem(window
, nav_bb_arg
? *nav_bb_arg
: bb
, id
);
2669 window
->DC
.LastItemId
= id
;
2670 window
->DC
.LastItemRect
= bb
;
2671 window
->DC
.LastItemStatusFlags
= 0;
2674 const bool is_clipped
= IsClippedEx(bb
, id
, false);
2677 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2679 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2680 if (IsMouseHoveringRect(bb
.Min
, bb
.Max
))
2681 window
->DC
.LastItemStatusFlags
|= ImGuiItemStatusFlags_HoveredRect
;
2685 // This is roughly matching the behavior of internal-facing ItemHoverable()
2686 // - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered()
2687 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
2688 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags
)
2690 ImGuiContext
& g
= *GImGui
;
2691 ImGuiWindow
* window
= g
.CurrentWindow
;
2692 if (g
.NavDisableMouseHover
&& !g
.NavDisableHighlight
)
2693 return IsItemFocused();
2695 // Test for bounding box overlap, as updated as ItemAdd()
2696 if (!(window
->DC
.LastItemStatusFlags
& ImGuiItemStatusFlags_HoveredRect
))
2698 IM_ASSERT((flags
& (ImGuiHoveredFlags_RootWindow
| ImGuiHoveredFlags_ChildWindows
)) == 0); // Flags not supported by this function
2700 // Test if we are hovering the right window (our window could be behind another window)
2701 // [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.
2702 // 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.
2703 //if (g.HoveredWindow != window)
2705 if (g
.HoveredRootWindow
!= window
->RootWindow
&& !(flags
& ImGuiHoveredFlags_AllowWhenOverlapped
))
2708 // Test if another item is active (e.g. being dragged)
2709 if (!(flags
& ImGuiHoveredFlags_AllowWhenBlockedByActiveItem
))
2710 if (g
.ActiveId
!= 0 && g
.ActiveId
!= window
->DC
.LastItemId
&& !g
.ActiveIdAllowOverlap
&& g
.ActiveId
!= window
->MoveId
)
2713 // Test if interactions on this window are blocked by an active popup or modal
2714 if (!IsWindowContentHoverable(window
, flags
))
2717 // Test if the item is disabled
2718 if ((window
->DC
.ItemFlags
& ImGuiItemFlags_Disabled
) && !(flags
& ImGuiHoveredFlags_AllowWhenDisabled
))
2721 // 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.
2722 if (window
->DC
.LastItemId
== window
->MoveId
&& window
->WriteAccessed
)
2727 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
2728 bool ImGui::ItemHoverable(const ImRect
& bb
, ImGuiID id
)
2730 ImGuiContext
& g
= *GImGui
;
2731 if (g
.HoveredId
!= 0 && g
.HoveredId
!= id
&& !g
.HoveredIdAllowOverlap
)
2734 ImGuiWindow
* window
= g
.CurrentWindow
;
2735 if (g
.HoveredWindow
!= window
)
2737 if (g
.ActiveId
!= 0 && g
.ActiveId
!= id
&& !g
.ActiveIdAllowOverlap
)
2739 if (!IsMouseHoveringRect(bb
.Min
, bb
.Max
))
2741 if (g
.NavDisableMouseHover
|| !IsWindowContentHoverable(window
, ImGuiHoveredFlags_None
))
2743 if (window
->DC
.ItemFlags
& ImGuiItemFlags_Disabled
)
2750 bool ImGui::IsClippedEx(const ImRect
& bb
, ImGuiID id
, bool clip_even_when_logged
)
2752 ImGuiContext
& g
= *GImGui
;
2753 ImGuiWindow
* window
= g
.CurrentWindow
;
2754 if (!bb
.Overlaps(window
->ClipRect
))
2755 if (id
== 0 || id
!= g
.ActiveId
)
2756 if (clip_even_when_logged
|| !g
.LogEnabled
)
2761 bool ImGui::FocusableItemRegister(ImGuiWindow
* window
, ImGuiID id
, bool tab_stop
)
2763 ImGuiContext
& g
= *GImGui
;
2765 const bool allow_keyboard_focus
= (window
->DC
.ItemFlags
& (ImGuiItemFlags_AllowKeyboardFocus
| ImGuiItemFlags_Disabled
)) == ImGuiItemFlags_AllowKeyboardFocus
;
2766 window
->FocusIdxAllCounter
++;
2767 if (allow_keyboard_focus
)
2768 window
->FocusIdxTabCounter
++;
2770 // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item.
2771 // Note that we can always TAB out of a widget that doesn't allow tabbing in.
2772 if (tab_stop
&& (g
.ActiveId
== id
) && window
->FocusIdxAllRequestNext
== INT_MAX
&& window
->FocusIdxTabRequestNext
== INT_MAX
&& !g
.IO
.KeyCtrl
&& IsKeyPressedMap(ImGuiKey_Tab
))
2773 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.
2775 if (window
->FocusIdxAllCounter
== window
->FocusIdxAllRequestCurrent
)
2777 if (allow_keyboard_focus
&& window
->FocusIdxTabCounter
== window
->FocusIdxTabRequestCurrent
)
2779 g
.NavJustTabbedId
= id
;
2786 void ImGui::FocusableItemUnregister(ImGuiWindow
* window
)
2788 window
->FocusIdxAllCounter
--;
2789 window
->FocusIdxTabCounter
--;
2792 ImVec2
ImGui::CalcItemSize(ImVec2 size
, float default_x
, float default_y
)
2794 ImGuiContext
& g
= *GImGui
;
2796 if (size
.x
< 0.0f
|| size
.y
< 0.0f
)
2797 content_max
= g
.CurrentWindow
->Pos
+ GetContentRegionMax();
2799 size
.x
= (size
.x
== 0.0f
) ? default_x
: ImMax(content_max
.x
- g
.CurrentWindow
->DC
.CursorPos
.x
, 4.0f
) + size
.x
;
2801 size
.y
= (size
.y
== 0.0f
) ? default_y
: ImMax(content_max
.y
- g
.CurrentWindow
->DC
.CursorPos
.y
, 4.0f
) + size
.y
;
2805 float ImGui::CalcWrapWidthForPos(const ImVec2
& pos
, float wrap_pos_x
)
2807 if (wrap_pos_x
< 0.0f
)
2810 ImGuiWindow
* window
= GetCurrentWindowRead();
2811 if (wrap_pos_x
== 0.0f
)
2812 wrap_pos_x
= GetContentRegionMax().x
+ window
->Pos
.x
;
2813 else if (wrap_pos_x
> 0.0f
)
2814 wrap_pos_x
+= window
->Pos
.x
- window
->Scroll
.x
; // wrap_pos_x is provided is window local space
2816 return ImMax(wrap_pos_x
- pos
.x
, 1.0f
);
2819 //-----------------------------------------------------------------------------
2821 void* ImGui::MemAlloc(size_t size
)
2823 GImAllocatorActiveAllocationsCount
++;
2824 return GImAllocatorAllocFunc(size
, GImAllocatorUserData
);
2827 void ImGui::MemFree(void* ptr
)
2829 if (ptr
) GImAllocatorActiveAllocationsCount
--;
2830 return GImAllocatorFreeFunc(ptr
, GImAllocatorUserData
);
2833 const char* ImGui::GetClipboardText()
2835 return GImGui
->IO
.GetClipboardTextFn
? GImGui
->IO
.GetClipboardTextFn(GImGui
->IO
.ClipboardUserData
) : "";
2838 void ImGui::SetClipboardText(const char* text
)
2840 if (GImGui
->IO
.SetClipboardTextFn
)
2841 GImGui
->IO
.SetClipboardTextFn(GImGui
->IO
.ClipboardUserData
, text
);
2844 const char* ImGui::GetVersion()
2846 return IMGUI_VERSION
;
2849 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
2850 // 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
2851 ImGuiContext
* ImGui::GetCurrentContext()
2856 void ImGui::SetCurrentContext(ImGuiContext
* ctx
)
2858 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
2859 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx
); // For custom thread-based hackery you may want to have control over this.
2865 // Helper function to verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
2866 // 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.
2867 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
)
2870 if (strcmp(version
, IMGUI_VERSION
)!=0) { error
= true; IM_ASSERT(strcmp(version
,IMGUI_VERSION
)==0 && "Mismatch version string!"); }
2871 if (sz_io
!= sizeof(ImGuiIO
)) { error
= true; IM_ASSERT(sz_io
== sizeof(ImGuiIO
) && "Mismatched struct layout!"); }
2872 if (sz_style
!= sizeof(ImGuiStyle
)) { error
= true; IM_ASSERT(sz_style
== sizeof(ImGuiStyle
) && "Mismatched struct layout!"); }
2873 if (sz_vec2
!= sizeof(ImVec2
)) { error
= true; IM_ASSERT(sz_vec2
== sizeof(ImVec2
) && "Mismatched struct layout!"); }
2874 if (sz_vec4
!= sizeof(ImVec4
)) { error
= true; IM_ASSERT(sz_vec4
== sizeof(ImVec4
) && "Mismatched struct layout!"); }
2875 if (sz_vert
!= sizeof(ImDrawVert
)) { error
= true; IM_ASSERT(sz_vert
== sizeof(ImDrawVert
) && "Mismatched struct layout!"); }
2879 void ImGui::SetAllocatorFunctions(void* (*alloc_func
)(size_t sz
, void* user_data
), void(*free_func
)(void* ptr
, void* user_data
), void* user_data
)
2881 GImAllocatorAllocFunc
= alloc_func
;
2882 GImAllocatorFreeFunc
= free_func
;
2883 GImAllocatorUserData
= user_data
;
2886 ImGuiContext
* ImGui::CreateContext(ImFontAtlas
* shared_font_atlas
)
2888 ImGuiContext
* ctx
= IM_NEW(ImGuiContext
)(shared_font_atlas
);
2890 SetCurrentContext(ctx
);
2895 void ImGui::DestroyContext(ImGuiContext
* ctx
)
2901 SetCurrentContext(NULL
);
2905 ImGuiIO
& ImGui::GetIO()
2907 IM_ASSERT(GImGui
!= NULL
&& "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2911 ImGuiStyle
& ImGui::GetStyle()
2913 IM_ASSERT(GImGui
!= NULL
&& "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2914 return GImGui
->Style
;
2917 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
2918 ImDrawData
* ImGui::GetDrawData()
2920 ImGuiContext
& g
= *GImGui
;
2921 return g
.DrawData
.Valid
? &g
.DrawData
: NULL
;
2924 double ImGui::GetTime()
2926 return GImGui
->Time
;
2929 int ImGui::GetFrameCount()
2931 return GImGui
->FrameCount
;
2934 ImDrawList
* ImGui::GetOverlayDrawList()
2936 return &GImGui
->OverlayDrawList
;
2939 ImDrawListSharedData
* ImGui::GetDrawListSharedData()
2941 return &GImGui
->DrawListSharedData
;
2944 // This needs to be called before we submit any widget (aka in or before Begin)
2945 void ImGui::NavInitWindow(ImGuiWindow
* window
, bool force_reinit
)
2947 ImGuiContext
& g
= *GImGui
;
2948 IM_ASSERT(window
== g
.NavWindow
);
2949 bool init_for_nav
= false;
2950 if (!(window
->Flags
& ImGuiWindowFlags_NoNavInputs
))
2951 if (!(window
->Flags
& ImGuiWindowFlags_ChildWindow
) || (window
->Flags
& ImGuiWindowFlags_Popup
) || (window
->NavLastIds
[0] == 0) || force_reinit
)
2952 init_for_nav
= true;
2955 SetNavID(0, g
.NavLayer
);
2956 g
.NavInitRequest
= true;
2957 g
.NavInitRequestFromMove
= false;
2958 g
.NavInitResultId
= 0;
2959 g
.NavInitResultRectRel
= ImRect();
2960 NavUpdateAnyRequestFlag();
2964 g
.NavId
= window
->NavLastIds
[0];
2968 static ImVec2
NavCalcPreferredRefPos()
2970 ImGuiContext
& g
= *GImGui
;
2971 if (g
.NavDisableHighlight
|| !g
.NavDisableMouseHover
|| !g
.NavWindow
)
2972 return ImFloor(g
.IO
.MousePos
);
2974 // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item
2975 const ImRect
& rect_rel
= g
.NavWindow
->NavRectRel
[g
.NavLayer
];
2976 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()));
2977 ImRect visible_rect
= GetViewportRect();
2978 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.
2981 static int FindWindowIndex(ImGuiWindow
* window
) // FIXME-OPT O(N)
2983 ImGuiContext
& g
= *GImGui
;
2984 for (int i
= g
.Windows
.Size
-1; i
>= 0; i
--)
2985 if (g
.Windows
[i
] == window
)
2990 static ImGuiWindow
* FindWindowNavigable(int i_start
, int i_stop
, int dir
) // FIXME-OPT O(N)
2992 ImGuiContext
& g
= *GImGui
;
2993 for (int i
= i_start
; i
>= 0 && i
< g
.Windows
.Size
&& i
!= i_stop
; i
+= dir
)
2994 if (ImGui::IsWindowNavFocusable(g
.Windows
[i
]))
2995 return g
.Windows
[i
];
2999 float ImGui::GetNavInputAmount(ImGuiNavInput n
, ImGuiInputReadMode mode
)
3001 ImGuiContext
& g
= *GImGui
;
3002 if (mode
== ImGuiInputReadMode_Down
)
3003 return g
.IO
.NavInputs
[n
]; // Instant, read analog input (0.0f..1.0f, as provided by user)
3005 const float t
= g
.IO
.NavInputsDownDuration
[n
];
3006 if (t
< 0.0f
&& mode
== ImGuiInputReadMode_Released
) // Return 1.0f when just released, no repeat, ignore analog input.
3007 return (g
.IO
.NavInputsDownDurationPrev
[n
] >= 0.0f
? 1.0f
: 0.0f
);
3010 if (mode
== ImGuiInputReadMode_Pressed
) // Return 1.0f when just pressed, no repeat, ignore analog input.
3011 return (t
== 0.0f
) ? 1.0f
: 0.0f
;
3012 if (mode
== ImGuiInputReadMode_Repeat
)
3013 return (float)CalcTypematicPressedRepeatAmount(t
, t
- g
.IO
.DeltaTime
, g
.IO
.KeyRepeatDelay
* 0.80f
, g
.IO
.KeyRepeatRate
* 0.80f
);
3014 if (mode
== ImGuiInputReadMode_RepeatSlow
)
3015 return (float)CalcTypematicPressedRepeatAmount(t
, t
- g
.IO
.DeltaTime
, g
.IO
.KeyRepeatDelay
* 1.00f
, g
.IO
.KeyRepeatRate
* 2.00f
);
3016 if (mode
== ImGuiInputReadMode_RepeatFast
)
3017 return (float)CalcTypematicPressedRepeatAmount(t
, t
- g
.IO
.DeltaTime
, g
.IO
.KeyRepeatDelay
* 0.80f
, g
.IO
.KeyRepeatRate
* 0.30f
);
3021 // Equivalent of IsKeyDown() for NavInputs[]
3022 static bool IsNavInputDown(ImGuiNavInput n
)
3024 return GImGui
->IO
.NavInputs
[n
] > 0.0f
;
3027 // Equivalent of IsKeyPressed() for NavInputs[]
3028 static bool IsNavInputPressed(ImGuiNavInput n
, ImGuiInputReadMode mode
)
3030 return ImGui::GetNavInputAmount(n
, mode
) > 0.0f
;
3033 static bool IsNavInputPressedAnyOfTwo(ImGuiNavInput n1
, ImGuiNavInput n2
, ImGuiInputReadMode mode
)
3035 return (ImGui::GetNavInputAmount(n1
, mode
) + ImGui::GetNavInputAmount(n2
, mode
)) > 0.0f
;
3038 ImVec2
ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources
, ImGuiInputReadMode mode
, float slow_factor
, float fast_factor
)
3040 ImVec2
delta(0.0f
, 0.0f
);
3041 if (dir_sources
& ImGuiNavDirSourceFlags_Keyboard
)
3042 delta
+= ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_
, mode
) - GetNavInputAmount(ImGuiNavInput_KeyLeft_
, mode
), GetNavInputAmount(ImGuiNavInput_KeyDown_
, mode
) - GetNavInputAmount(ImGuiNavInput_KeyUp_
, mode
));
3043 if (dir_sources
& ImGuiNavDirSourceFlags_PadDPad
)
3044 delta
+= ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight
, mode
) - GetNavInputAmount(ImGuiNavInput_DpadLeft
, mode
), GetNavInputAmount(ImGuiNavInput_DpadDown
, mode
) - GetNavInputAmount(ImGuiNavInput_DpadUp
, mode
));
3045 if (dir_sources
& ImGuiNavDirSourceFlags_PadLStick
)
3046 delta
+= ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight
, mode
) - GetNavInputAmount(ImGuiNavInput_LStickLeft
, mode
), GetNavInputAmount(ImGuiNavInput_LStickDown
, mode
) - GetNavInputAmount(ImGuiNavInput_LStickUp
, mode
));
3047 if (slow_factor
!= 0.0f
&& IsNavInputDown(ImGuiNavInput_TweakSlow
))
3048 delta
*= slow_factor
;
3049 if (fast_factor
!= 0.0f
&& IsNavInputDown(ImGuiNavInput_TweakFast
))
3050 delta
*= fast_factor
;
3054 static void NavUpdateWindowingHighlightWindow(int focus_change_dir
)
3056 ImGuiContext
& g
= *GImGui
;
3057 IM_ASSERT(g
.NavWindowingTarget
);
3058 if (g
.NavWindowingTarget
->Flags
& ImGuiWindowFlags_Modal
)
3061 const int i_current
= FindWindowIndex(g
.NavWindowingTarget
);
3062 ImGuiWindow
* window_target
= FindWindowNavigable(i_current
+ focus_change_dir
, -INT_MAX
, focus_change_dir
);
3064 window_target
= FindWindowNavigable((focus_change_dir
< 0) ? (g
.Windows
.Size
- 1) : 0, i_current
, focus_change_dir
);
3065 if (window_target
) // Don't reset windowing target if there's a single window in the list
3066 g
.NavWindowingTarget
= g
.NavWindowingTargetAnim
= window_target
;
3067 g
.NavWindowingToggleLayer
= false;
3070 // Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer)
3071 static void ImGui::NavUpdateWindowing()
3073 ImGuiContext
& g
= *GImGui
;
3074 ImGuiWindow
* apply_focus_window
= NULL
;
3075 bool apply_toggle_layer
= false;
3077 ImGuiWindow
* modal_window
= GetFrontMostPopupModal();
3078 if (modal_window
!= NULL
)
3080 g
.NavWindowingTarget
= NULL
;
3085 if (g
.NavWindowingTargetAnim
&& g
.NavWindowingTarget
== NULL
)
3087 g
.NavWindowingHighlightAlpha
= ImMax(g
.NavWindowingHighlightAlpha
- g
.IO
.DeltaTime
* 10.0f
, 0.0f
);
3088 if (g
.DimBgRatio
<= 0.0f
&& g
.NavWindowingHighlightAlpha
<= 0.0f
)
3089 g
.NavWindowingTargetAnim
= NULL
;
3092 // Start CTRL-TAB or Square+L/R window selection
3093 bool start_windowing_with_gamepad
= !g
.NavWindowingTarget
&& IsNavInputPressed(ImGuiNavInput_Menu
, ImGuiInputReadMode_Pressed
);
3094 bool start_windowing_with_keyboard
= !g
.NavWindowingTarget
&& g
.IO
.KeyCtrl
&& IsKeyPressedMap(ImGuiKey_Tab
) && (g
.IO
.ConfigFlags
& ImGuiConfigFlags_NavEnableKeyboard
);
3095 if (start_windowing_with_gamepad
|| start_windowing_with_keyboard
)
3096 if (ImGuiWindow
* window
= g
.NavWindow
? g
.NavWindow
: FindWindowNavigable(g
.Windows
.Size
- 1, -INT_MAX
, -1))
3098 g
.NavWindowingTarget
= g
.NavWindowingTargetAnim
= window
;
3099 g
.NavWindowingTimer
= g
.NavWindowingHighlightAlpha
= 0.0f
;
3100 g
.NavWindowingToggleLayer
= start_windowing_with_keyboard
? false : true;
3101 g
.NavInputSource
= start_windowing_with_keyboard
? ImGuiInputSource_NavKeyboard
: ImGuiInputSource_NavGamepad
;
3105 g
.NavWindowingTimer
+= g
.IO
.DeltaTime
;
3106 if (g
.NavWindowingTarget
&& g
.NavInputSource
== ImGuiInputSource_NavGamepad
)
3108 // 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
3109 g
.NavWindowingHighlightAlpha
= ImMax(g
.NavWindowingHighlightAlpha
, ImSaturate((g
.NavWindowingTimer
- NAV_WINDOWING_HIGHLIGHT_DELAY
) / 0.05f
));
3111 // Select window to focus
3112 const int focus_change_dir
= (int)IsNavInputPressed(ImGuiNavInput_FocusPrev
, ImGuiInputReadMode_RepeatSlow
) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext
, ImGuiInputReadMode_RepeatSlow
);
3113 if (focus_change_dir
!= 0)
3115 NavUpdateWindowingHighlightWindow(focus_change_dir
);
3116 g
.NavWindowingHighlightAlpha
= 1.0f
;
3119 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most)
3120 if (!IsNavInputDown(ImGuiNavInput_Menu
))
3122 g
.NavWindowingToggleLayer
&= (g
.NavWindowingHighlightAlpha
< 1.0f
); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
3123 if (g
.NavWindowingToggleLayer
&& g
.NavWindow
)
3124 apply_toggle_layer
= true;
3125 else if (!g
.NavWindowingToggleLayer
)
3126 apply_focus_window
= g
.NavWindowingTarget
;
3127 g
.NavWindowingTarget
= NULL
;
3132 if (g
.NavWindowingTarget
&& g
.NavInputSource
== ImGuiInputSource_NavKeyboard
)
3134 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
3135 g
.NavWindowingHighlightAlpha
= ImMax(g
.NavWindowingHighlightAlpha
, ImSaturate((g
.NavWindowingTimer
- NAV_WINDOWING_HIGHLIGHT_DELAY
) / 0.05f
)); // 1.0f
3136 if (IsKeyPressedMap(ImGuiKey_Tab
, true))
3137 NavUpdateWindowingHighlightWindow(g
.IO
.KeyShift
? +1 : -1);
3139 apply_focus_window
= g
.NavWindowingTarget
;
3142 // Keyboard: Press and Release ALT to toggle menu layer
3143 // 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
3144 if ((g
.ActiveId
== 0 || g
.ActiveIdAllowOverlap
) && IsNavInputPressed(ImGuiNavInput_KeyMenu_
, ImGuiInputReadMode_Released
))
3145 if (IsMousePosValid(&g
.IO
.MousePos
) == IsMousePosValid(&g
.IO
.MousePosPrev
))
3146 apply_toggle_layer
= true;
3149 if (g
.NavWindowingTarget
&& !(g
.NavWindowingTarget
->Flags
& ImGuiWindowFlags_NoMove
))
3152 if (g
.NavInputSource
== ImGuiInputSource_NavKeyboard
&& !g
.IO
.KeyShift
)
3153 move_delta
= GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard
, ImGuiInputReadMode_Down
);
3154 if (g
.NavInputSource
== ImGuiInputSource_NavGamepad
)
3155 move_delta
= GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick
, ImGuiInputReadMode_Down
);
3156 if (move_delta
.x
!= 0.0f
|| move_delta
.y
!= 0.0f
)
3158 const float NAV_MOVE_SPEED
= 800.0f
;
3159 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
3160 g
.NavWindowingTarget
->RootWindow
->Pos
+= move_delta
* move_speed
;
3161 g
.NavDisableMouseHover
= true;
3162 MarkIniSettingsDirty(g
.NavWindowingTarget
);
3166 // Apply final focus
3167 if (apply_focus_window
&& (g
.NavWindow
== NULL
|| apply_focus_window
!= g
.NavWindow
->RootWindow
))
3169 g
.NavDisableHighlight
= false;
3170 g
.NavDisableMouseHover
= true;
3171 apply_focus_window
= NavRestoreLastChildNavWindow(apply_focus_window
);
3172 ClosePopupsOverWindow(apply_focus_window
);
3173 FocusWindow(apply_focus_window
);
3174 if (apply_focus_window
->NavLastIds
[0] == 0)
3175 NavInitWindow(apply_focus_window
, false);
3177 // If the window only has a menu layer, select it directly
3178 if (apply_focus_window
->DC
.NavLayerActiveMask
== (1 << 1))
3181 if (apply_focus_window
)
3182 g
.NavWindowingTarget
= NULL
;
3184 // Apply menu/layer toggle
3185 if (apply_toggle_layer
&& g
.NavWindow
)
3187 // Move to parent menu if necessary
3188 ImGuiWindow
* new_nav_window
= g
.NavWindow
;
3189 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)
3190 new_nav_window
= new_nav_window
->ParentWindow
;
3192 if (new_nav_window
!= g
.NavWindow
)
3194 ImGuiWindow
* old_nav_window
= g
.NavWindow
;
3195 FocusWindow(new_nav_window
);
3196 new_nav_window
->NavLastChildNavWindow
= old_nav_window
;
3198 g
.NavDisableHighlight
= false;
3199 g
.NavDisableMouseHover
= true;
3200 NavRestoreLayer((g
.NavWindow
->DC
.NavLayerActiveMask
& (1 << 1)) ? (g
.NavLayer
^ 1) : 0);
3204 // Window has already passed the IsWindowNavFocusable()
3205 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow
* window
)
3207 if (window
->Flags
& ImGuiWindowFlags_Popup
)
3209 if ((window
->Flags
& ImGuiWindowFlags_MenuBar
) && strcmp(window
->Name
, "##MainMenuBar") == 0)
3210 return "(Main menu bar)";
3211 return "(Untitled)";
3214 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
3215 void ImGui::NavUpdateWindowingList()
3217 ImGuiContext
& g
= *GImGui
;
3218 IM_ASSERT(g
.NavWindowingTarget
!= NULL
);
3220 if (g
.NavWindowingTimer
< NAV_WINDOWING_LIST_APPEAR_DELAY
)
3223 if (g
.NavWindowingList
== NULL
)
3224 g
.NavWindowingList
= FindWindowByName("###NavWindowingList");
3225 SetNextWindowSizeConstraints(ImVec2(g
.IO
.DisplaySize
.x
* 0.20f
, g
.IO
.DisplaySize
.y
* 0.20f
), ImVec2(FLT_MAX
, FLT_MAX
));
3226 SetNextWindowPos(g
.IO
.DisplaySize
* 0.5f
, ImGuiCond_Always
, ImVec2(0.5f
, 0.5f
));
3227 PushStyleVar(ImGuiStyleVar_WindowPadding
, g
.Style
.WindowPadding
* 2.0f
);
3228 Begin("###NavWindowingList", NULL
, ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoFocusOnAppearing
| ImGuiWindowFlags_NoNav
| ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoInputs
| ImGuiWindowFlags_AlwaysAutoResize
);
3229 for (int n
= g
.Windows
.Size
- 1; n
>= 0; n
--)
3231 ImGuiWindow
* window
= g
.Windows
[n
];
3232 if (!IsWindowNavFocusable(window
))
3234 const char* label
= window
->Name
;
3235 if (label
== FindRenderedTextEnd(label
))
3236 label
= GetFallbackWindowNameForWindowingList(window
);
3237 Selectable(label
, g
.NavWindowingTarget
== window
);
3243 // Scroll to keep newly navigated item fully into view
3244 // NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated.
3245 static void NavScrollToBringItemIntoView(ImGuiWindow
* window
, const ImRect
& item_rect
)
3247 ImRect
window_rect(window
->InnerMainRect
.Min
- ImVec2(1, 1), window
->InnerMainRect
.Max
+ ImVec2(1, 1));
3248 //g.OverlayDrawList.AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
3249 if (window_rect
.Contains(item_rect
))
3252 ImGuiContext
& g
= *GImGui
;
3253 if (window
->ScrollbarX
&& item_rect
.Min
.x
< window_rect
.Min
.x
)
3255 window
->ScrollTarget
.x
= item_rect
.Min
.x
- window
->Pos
.x
+ window
->Scroll
.x
- g
.Style
.ItemSpacing
.x
;
3256 window
->ScrollTargetCenterRatio
.x
= 0.0f
;
3258 else if (window
->ScrollbarX
&& item_rect
.Max
.x
>= window_rect
.Max
.x
)
3260 window
->ScrollTarget
.x
= item_rect
.Max
.x
- window
->Pos
.x
+ window
->Scroll
.x
+ g
.Style
.ItemSpacing
.x
;
3261 window
->ScrollTargetCenterRatio
.x
= 1.0f
;
3263 if (item_rect
.Min
.y
< window_rect
.Min
.y
)
3265 window
->ScrollTarget
.y
= item_rect
.Min
.y
- window
->Pos
.y
+ window
->Scroll
.y
- g
.Style
.ItemSpacing
.y
;
3266 window
->ScrollTargetCenterRatio
.y
= 0.0f
;
3268 else if (item_rect
.Max
.y
>= window_rect
.Max
.y
)
3270 window
->ScrollTarget
.y
= item_rect
.Max
.y
- window
->Pos
.y
+ window
->Scroll
.y
+ g
.Style
.ItemSpacing
.y
;
3271 window
->ScrollTargetCenterRatio
.y
= 1.0f
;
3275 static void ImGui::NavUpdate()
3277 ImGuiContext
& g
= *GImGui
;
3278 g
.IO
.WantSetMousePos
= false;
3281 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
);
3284 bool nav_keyboard_active
= (g
.IO
.ConfigFlags
& ImGuiConfigFlags_NavEnableKeyboard
) != 0;
3285 bool nav_gamepad_active
= (g
.IO
.ConfigFlags
& ImGuiConfigFlags_NavEnableGamepad
) != 0 && (g
.IO
.BackendFlags
& ImGuiBackendFlags_HasGamepad
) != 0;
3287 // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard)
3288 if (nav_gamepad_active
)
3289 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
)
3290 g
.NavInputSource
= ImGuiInputSource_NavGamepad
;
3292 // Update Keyboard->Nav inputs mapping
3293 if (nav_keyboard_active
)
3295 #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; }
3296 NAV_MAP_KEY(ImGuiKey_Space
, ImGuiNavInput_Activate
);
3297 NAV_MAP_KEY(ImGuiKey_Enter
, ImGuiNavInput_Input
);
3298 NAV_MAP_KEY(ImGuiKey_Escape
, ImGuiNavInput_Cancel
);
3299 NAV_MAP_KEY(ImGuiKey_LeftArrow
, ImGuiNavInput_KeyLeft_
);
3300 NAV_MAP_KEY(ImGuiKey_RightArrow
,ImGuiNavInput_KeyRight_
);
3301 NAV_MAP_KEY(ImGuiKey_UpArrow
, ImGuiNavInput_KeyUp_
);
3302 NAV_MAP_KEY(ImGuiKey_DownArrow
, ImGuiNavInput_KeyDown_
);
3303 if (g
.IO
.KeyCtrl
) g
.IO
.NavInputs
[ImGuiNavInput_TweakSlow
] = 1.0f
;
3304 if (g
.IO
.KeyShift
) g
.IO
.NavInputs
[ImGuiNavInput_TweakFast
] = 1.0f
;
3305 if (g
.IO
.KeyAlt
) g
.IO
.NavInputs
[ImGuiNavInput_KeyMenu_
] = 1.0f
;
3309 memcpy(g
.IO
.NavInputsDownDurationPrev
, g
.IO
.NavInputsDownDuration
, sizeof(g
.IO
.NavInputsDownDuration
));
3310 for (int i
= 0; i
< IM_ARRAYSIZE(g
.IO
.NavInputs
); i
++)
3311 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
;
3313 // Process navigation init request (select first/default focus)
3314 if (g
.NavInitResultId
!= 0 && (!g
.NavDisableHighlight
|| g
.NavInitRequestFromMove
))
3316 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
3317 IM_ASSERT(g
.NavWindow
);
3318 if (g
.NavInitRequestFromMove
)
3319 SetNavIDWithRectRel(g
.NavInitResultId
, g
.NavLayer
, g
.NavInitResultRectRel
);
3321 SetNavID(g
.NavInitResultId
, g
.NavLayer
);
3322 g
.NavWindow
->NavRectRel
[g
.NavLayer
] = g
.NavInitResultRectRel
;
3324 g
.NavInitRequest
= false;
3325 g
.NavInitRequestFromMove
= false;
3326 g
.NavInitResultId
= 0;
3327 g
.NavJustMovedToId
= 0;
3329 // Process navigation move request
3330 if (g
.NavMoveRequest
&& (g
.NavMoveResultLocal
.ID
!= 0 || g
.NavMoveResultOther
.ID
!= 0))
3332 // Select which result to use
3333 ImGuiNavMoveResult
* result
= (g
.NavMoveResultLocal
.ID
!= 0) ? &g
.NavMoveResultLocal
: &g
.NavMoveResultOther
;
3335 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
3336 if (g
.NavMoveRequestFlags
& ImGuiNavMoveFlags_AlsoScoreVisibleSet
)
3337 if (g
.NavMoveResultLocalVisibleSet
.ID
!= 0 && g
.NavMoveResultLocalVisibleSet
.ID
!= g
.NavId
)
3338 result
= &g
.NavMoveResultLocalVisibleSet
;
3340 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
3341 if (result
!= &g
.NavMoveResultOther
&& g
.NavMoveResultOther
.ID
!= 0 && g
.NavMoveResultOther
.Window
->ParentWindow
== g
.NavWindow
)
3342 if ((g
.NavMoveResultOther
.DistBox
< result
->DistBox
) || (g
.NavMoveResultOther
.DistBox
== result
->DistBox
&& g
.NavMoveResultOther
.DistCenter
< result
->DistCenter
))
3343 result
= &g
.NavMoveResultOther
;
3344 IM_ASSERT(g
.NavWindow
&& result
->Window
);
3346 // Scroll to keep newly navigated item fully into view.
3347 if (g
.NavLayer
== 0)
3349 ImRect rect_abs
= ImRect(result
->RectRel
.Min
+ result
->Window
->Pos
, result
->RectRel
.Max
+ result
->Window
->Pos
);
3350 NavScrollToBringItemIntoView(result
->Window
, rect_abs
);
3352 // Estimate upcoming scroll so we can offset our result position so mouse position can be applied immediately after in NavUpdate()
3353 ImVec2 next_scroll
= CalcNextScrollFromScrollTargetAndClamp(result
->Window
, false);
3354 ImVec2 delta_scroll
= result
->Window
->Scroll
- next_scroll
;
3355 result
->RectRel
.Translate(delta_scroll
);
3357 // Also scroll parent window to keep us into view if necessary (we could/should technically recurse back the whole the parent hierarchy).
3358 if (result
->Window
->Flags
& ImGuiWindowFlags_ChildWindow
)
3359 NavScrollToBringItemIntoView(result
->Window
->ParentWindow
, ImRect(rect_abs
.Min
+ delta_scroll
, rect_abs
.Max
+ delta_scroll
));
3362 // Apply result from previous frame navigation directional move request
3364 g
.NavWindow
= result
->Window
;
3365 SetNavIDWithRectRel(result
->ID
, g
.NavLayer
, result
->RectRel
);
3366 g
.NavJustMovedToId
= result
->ID
;
3367 g
.NavMoveFromClampedRefRect
= false;
3370 // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
3371 if (g
.NavMoveRequestForward
== ImGuiNavForward_ForwardActive
)
3373 IM_ASSERT(g
.NavMoveRequest
);
3374 if (g
.NavMoveResultLocal
.ID
== 0 && g
.NavMoveResultOther
.ID
== 0)
3375 g
.NavDisableHighlight
= false;
3376 g
.NavMoveRequestForward
= ImGuiNavForward_None
;
3379 // Apply application mouse position movement, after we had a chance to process move request result.
3380 if (g
.NavMousePosDirty
&& g
.NavIdIsAlive
)
3382 // Set mouse position given our knowledge of the navigated item position from last frame
3383 if ((g
.IO
.ConfigFlags
& ImGuiConfigFlags_NavEnableSetMousePos
) && (g
.IO
.BackendFlags
& ImGuiBackendFlags_HasSetMousePos
))
3385 if (!g
.NavDisableHighlight
&& g
.NavDisableMouseHover
&& g
.NavWindow
)
3387 g
.IO
.MousePos
= g
.IO
.MousePosPrev
= NavCalcPreferredRefPos();
3388 g
.IO
.WantSetMousePos
= true;
3391 g
.NavMousePosDirty
= false;
3393 g
.NavIdIsAlive
= false;
3394 g
.NavJustTabbedId
= 0;
3395 IM_ASSERT(g
.NavLayer
== 0 || g
.NavLayer
== 1);
3397 // 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
3399 NavSaveLastChildNavWindow(g
.NavWindow
);
3400 if (g
.NavWindow
&& g
.NavWindow
->NavLastChildNavWindow
!= NULL
&& g
.NavLayer
== 0)
3401 g
.NavWindow
->NavLastChildNavWindow
= NULL
;
3403 NavUpdateWindowing();
3405 // Set output flags for user application
3406 g
.IO
.NavActive
= (nav_keyboard_active
|| nav_gamepad_active
) && g
.NavWindow
&& !(g
.NavWindow
->Flags
& ImGuiWindowFlags_NoNavInputs
);
3407 g
.IO
.NavVisible
= (g
.IO
.NavActive
&& g
.NavId
!= 0 && !g
.NavDisableHighlight
) || (g
.NavWindowingTarget
!= NULL
) || g
.NavInitRequest
;
3409 // Process NavCancel input (to close a popup, get back to parent, clear focus)
3410 if (IsNavInputPressed(ImGuiNavInput_Cancel
, ImGuiInputReadMode_Pressed
))
3412 if (g
.ActiveId
!= 0)
3416 else if (g
.NavWindow
&& (g
.NavWindow
->Flags
& ImGuiWindowFlags_ChildWindow
) && !(g
.NavWindow
->Flags
& ImGuiWindowFlags_Popup
) && g
.NavWindow
->ParentWindow
)
3418 // Exit child window
3419 ImGuiWindow
* child_window
= g
.NavWindow
;
3420 ImGuiWindow
* parent_window
= g
.NavWindow
->ParentWindow
;
3421 IM_ASSERT(child_window
->ChildId
!= 0);
3422 FocusWindow(parent_window
);
3423 SetNavID(child_window
->ChildId
, 0);
3424 g
.NavIdIsAlive
= false;
3425 if (g
.NavDisableMouseHover
)
3426 g
.NavMousePosDirty
= true;
3428 else if (g
.OpenPopupStack
.Size
> 0)
3430 // Close open popup/menu
3431 if (!(g
.OpenPopupStack
.back().Window
->Flags
& ImGuiWindowFlags_Modal
))
3432 ClosePopupToLevel(g
.OpenPopupStack
.Size
- 1);
3434 else if (g
.NavLayer
!= 0)
3436 // Leave the "menu" layer
3441 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
3442 if (g
.NavWindow
&& ((g
.NavWindow
->Flags
& ImGuiWindowFlags_Popup
) || !(g
.NavWindow
->Flags
& ImGuiWindowFlags_ChildWindow
)))
3443 g
.NavWindow
->NavLastIds
[0] = 0;
3448 // Process manual activation request
3449 g
.NavActivateId
= g
.NavActivateDownId
= g
.NavActivatePressedId
= g
.NavInputId
= 0;
3450 if (g
.NavId
!= 0 && !g
.NavDisableHighlight
&& !g
.NavWindowingTarget
&& g
.NavWindow
&& !(g
.NavWindow
->Flags
& ImGuiWindowFlags_NoNavInputs
))
3452 bool activate_down
= IsNavInputDown(ImGuiNavInput_Activate
);
3453 bool activate_pressed
= activate_down
&& IsNavInputPressed(ImGuiNavInput_Activate
, ImGuiInputReadMode_Pressed
);
3454 if (g
.ActiveId
== 0 && activate_pressed
)
3455 g
.NavActivateId
= g
.NavId
;
3456 if ((g
.ActiveId
== 0 || g
.ActiveId
== g
.NavId
) && activate_down
)
3457 g
.NavActivateDownId
= g
.NavId
;
3458 if ((g
.ActiveId
== 0 || g
.ActiveId
== g
.NavId
) && activate_pressed
)
3459 g
.NavActivatePressedId
= g
.NavId
;
3460 if ((g
.ActiveId
== 0 || g
.ActiveId
== g
.NavId
) && IsNavInputPressed(ImGuiNavInput_Input
, ImGuiInputReadMode_Pressed
))
3461 g
.NavInputId
= g
.NavId
;
3463 if (g
.NavWindow
&& (g
.NavWindow
->Flags
& ImGuiWindowFlags_NoNavInputs
))
3464 g
.NavDisableHighlight
= true;
3465 if (g
.NavActivateId
!= 0)
3466 IM_ASSERT(g
.NavActivateDownId
== g
.NavActivateId
);
3467 g
.NavMoveRequest
= false;
3469 // Process programmatic activation request
3470 if (g
.NavNextActivateId
!= 0)
3471 g
.NavActivateId
= g
.NavActivateDownId
= g
.NavActivatePressedId
= g
.NavInputId
= g
.NavNextActivateId
;
3472 g
.NavNextActivateId
= 0;
3474 // Initiate directional inputs request
3475 const int allowed_dir_flags
= (g
.ActiveId
== 0) ? ~0 : g
.ActiveIdAllowNavDirFlags
;
3476 if (g
.NavMoveRequestForward
== ImGuiNavForward_None
)
3478 g
.NavMoveDir
= ImGuiDir_None
;
3479 g
.NavMoveRequestFlags
= 0;
3480 if (g
.NavWindow
&& !g
.NavWindowingTarget
&& allowed_dir_flags
&& !(g
.NavWindow
->Flags
& ImGuiWindowFlags_NoNavInputs
))
3482 if ((allowed_dir_flags
& (1<<ImGuiDir_Left
)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft
, ImGuiNavInput_KeyLeft_
, ImGuiInputReadMode_Repeat
)) g
.NavMoveDir
= ImGuiDir_Left
;
3483 if ((allowed_dir_flags
& (1<<ImGuiDir_Right
)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight
,ImGuiNavInput_KeyRight_
,ImGuiInputReadMode_Repeat
)) g
.NavMoveDir
= ImGuiDir_Right
;
3484 if ((allowed_dir_flags
& (1<<ImGuiDir_Up
)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp
, ImGuiNavInput_KeyUp_
, ImGuiInputReadMode_Repeat
)) g
.NavMoveDir
= ImGuiDir_Up
;
3485 if ((allowed_dir_flags
& (1<<ImGuiDir_Down
)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown
, ImGuiNavInput_KeyDown_
, ImGuiInputReadMode_Repeat
)) g
.NavMoveDir
= ImGuiDir_Down
;
3487 g
.NavMoveClipDir
= g
.NavMoveDir
;
3491 // 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)
3492 // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
3493 IM_ASSERT(g
.NavMoveDir
!= ImGuiDir_None
&& g
.NavMoveClipDir
!= ImGuiDir_None
);
3494 IM_ASSERT(g
.NavMoveRequestForward
== ImGuiNavForward_ForwardQueued
);
3495 g
.NavMoveRequestForward
= ImGuiNavForward_ForwardActive
;
3498 // PageUp/PageDown scroll
3499 float nav_scoring_rect_offset_y
= 0.0f
;
3500 if (nav_keyboard_active
&& g
.NavMoveDir
== ImGuiDir_None
&& g
.NavWindow
&& !(g
.NavWindow
->Flags
& ImGuiWindowFlags_NoNavInputs
) && !g
.NavWindowingTarget
&& g
.NavLayer
== 0)
3502 ImGuiWindow
* window
= g
.NavWindow
;
3503 bool page_up_held
= IsKeyDown(g
.IO
.KeyMap
[ImGuiKey_PageUp
]) && (allowed_dir_flags
& (1 << ImGuiDir_Up
));
3504 bool page_down_held
= IsKeyDown(g
.IO
.KeyMap
[ImGuiKey_PageDown
]) && (allowed_dir_flags
& (1 << ImGuiDir_Down
));
3505 if ((page_up_held
&& !page_down_held
) || (page_down_held
&& !page_up_held
))
3507 if (window
->DC
.NavLayerActiveMask
== 0x00 && window
->DC
.NavHasScroll
)
3509 // Fallback manual-scroll when window has no navigable item
3510 if (IsKeyPressed(g
.IO
.KeyMap
[ImGuiKey_PageUp
], true))
3511 SetWindowScrollY(window
, window
->Scroll
.y
- window
->InnerClipRect
.GetHeight());
3512 else if (IsKeyPressed(g
.IO
.KeyMap
[ImGuiKey_PageDown
], true))
3513 SetWindowScrollY(window
, window
->Scroll
.y
+ window
->InnerClipRect
.GetHeight());
3517 const ImRect
& nav_rect_rel
= window
->NavRectRel
[g
.NavLayer
];
3518 const float page_offset_y
= ImMax(0.0f
, window
->InnerClipRect
.GetHeight() - window
->CalcFontSize() * 1.0f
+ nav_rect_rel
.GetHeight());
3519 if (IsKeyPressed(g
.IO
.KeyMap
[ImGuiKey_PageUp
], true))
3521 nav_scoring_rect_offset_y
= -page_offset_y
;
3522 g
.NavMoveDir
= ImGuiDir_Down
; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item)
3523 g
.NavMoveClipDir
= ImGuiDir_Up
;
3524 g
.NavMoveRequestFlags
= ImGuiNavMoveFlags_AllowCurrentNavId
| ImGuiNavMoveFlags_AlsoScoreVisibleSet
;
3526 else if (IsKeyPressed(g
.IO
.KeyMap
[ImGuiKey_PageDown
], true))
3528 nav_scoring_rect_offset_y
= +page_offset_y
;
3529 g
.NavMoveDir
= ImGuiDir_Up
; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item)
3530 g
.NavMoveClipDir
= ImGuiDir_Down
;
3531 g
.NavMoveRequestFlags
= ImGuiNavMoveFlags_AllowCurrentNavId
| ImGuiNavMoveFlags_AlsoScoreVisibleSet
;
3537 if (g
.NavMoveDir
!= ImGuiDir_None
)
3539 g
.NavMoveRequest
= true;
3540 g
.NavMoveDirLast
= g
.NavMoveDir
;
3543 // 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
3544 if (g
.NavMoveRequest
&& g
.NavId
== 0)
3546 g
.NavInitRequest
= g
.NavInitRequestFromMove
= true;
3547 g
.NavInitResultId
= 0;
3548 g
.NavDisableHighlight
= false;
3551 NavUpdateAnyRequestFlag();
3554 if (g
.NavWindow
&& !(g
.NavWindow
->Flags
& ImGuiWindowFlags_NoNavInputs
) && !g
.NavWindowingTarget
)
3556 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
3557 ImGuiWindow
* window
= g
.NavWindow
;
3558 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.
3559 if (window
->DC
.NavLayerActiveMask
== 0x00 && window
->DC
.NavHasScroll
&& g
.NavMoveRequest
)
3561 if (g
.NavMoveDir
== ImGuiDir_Left
|| g
.NavMoveDir
== ImGuiDir_Right
)
3562 SetWindowScrollX(window
, ImFloor(window
->Scroll
.x
+ ((g
.NavMoveDir
== ImGuiDir_Left
) ? -1.0f
: +1.0f
) * scroll_speed
));
3563 if (g
.NavMoveDir
== ImGuiDir_Up
|| g
.NavMoveDir
== ImGuiDir_Down
)
3564 SetWindowScrollY(window
, ImFloor(window
->Scroll
.y
+ ((g
.NavMoveDir
== ImGuiDir_Up
) ? -1.0f
: +1.0f
) * scroll_speed
));
3567 // *Normal* Manual scroll with NavScrollXXX keys
3568 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
3569 ImVec2 scroll_dir
= GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick
, ImGuiInputReadMode_Down
, 1.0f
/10.0f
, 10.0f
);
3570 if (scroll_dir
.x
!= 0.0f
&& window
->ScrollbarX
)
3572 SetWindowScrollX(window
, ImFloor(window
->Scroll
.x
+ scroll_dir
.x
* scroll_speed
));
3573 g
.NavMoveFromClampedRefRect
= true;
3575 if (scroll_dir
.y
!= 0.0f
)
3577 SetWindowScrollY(window
, ImFloor(window
->Scroll
.y
+ scroll_dir
.y
* scroll_speed
));
3578 g
.NavMoveFromClampedRefRect
= true;
3582 // Reset search results
3583 g
.NavMoveResultLocal
.Clear();
3584 g
.NavMoveResultLocalVisibleSet
.Clear();
3585 g
.NavMoveResultOther
.Clear();
3587 // 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
3588 if (g
.NavMoveRequest
&& g
.NavMoveFromClampedRefRect
&& g
.NavLayer
== 0)
3590 ImGuiWindow
* window
= g
.NavWindow
;
3591 ImRect
window_rect_rel(window
->InnerMainRect
.Min
- window
->Pos
- ImVec2(1,1), window
->InnerMainRect
.Max
- window
->Pos
+ ImVec2(1,1));
3592 if (!window_rect_rel
.Contains(window
->NavRectRel
[g
.NavLayer
]))
3594 float pad
= window
->CalcFontSize() * 0.5f
;
3595 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
3596 window
->NavRectRel
[g
.NavLayer
].ClipWith(window_rect_rel
);
3599 g
.NavMoveFromClampedRefRect
= false;
3602 // 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)
3603 ImRect nav_rect_rel
= (g
.NavWindow
&& !g
.NavWindow
->NavRectRel
[g
.NavLayer
].IsInverted()) ? g
.NavWindow
->NavRectRel
[g
.NavLayer
] : ImRect(0,0,0,0);
3604 g
.NavScoringRectScreen
= g
.NavWindow
? ImRect(g
.NavWindow
->Pos
+ nav_rect_rel
.Min
, g
.NavWindow
->Pos
+ nav_rect_rel
.Max
) : GetViewportRect();
3605 g
.NavScoringRectScreen
.TranslateY(nav_scoring_rect_offset_y
);
3606 g
.NavScoringRectScreen
.Min
.x
= ImMin(g
.NavScoringRectScreen
.Min
.x
+ 1.0f
, g
.NavScoringRectScreen
.Max
.x
);
3607 g
.NavScoringRectScreen
.Max
.x
= g
.NavScoringRectScreen
.Min
.x
;
3608 IM_ASSERT(!g
.NavScoringRectScreen
.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
3609 //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
3610 g
.NavScoringCount
= 0;
3611 #if IMGUI_DEBUG_NAV_RECTS
3612 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]
3613 if (g
.NavWindow
) { ImU32 col
= (g
.NavWindow
->HiddenFrames
== 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p
= NavCalcPreferredRefPos(); 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
); }
3617 void ImGui::StartMouseMovingWindow(ImGuiWindow
* window
)
3619 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3620 ImGuiContext
& g
= *GImGui
;
3621 FocusWindow(window
);
3622 SetActiveID(window
->MoveId
, window
);
3623 g
.NavDisableHighlight
= true;
3624 g
.ActiveIdClickOffset
= g
.IO
.MousePos
- window
->RootWindow
->Pos
;
3625 if (!(window
->Flags
& ImGuiWindowFlags_NoMove
) && !(window
->RootWindow
->Flags
& ImGuiWindowFlags_NoMove
))
3626 g
.MovingWindow
= window
;
3629 // Handle mouse moving window
3630 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
3631 void ImGui::UpdateMouseMovingWindow()
3633 ImGuiContext
& g
= *GImGui
;
3634 if (g
.MovingWindow
!= NULL
)
3636 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3637 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3638 KeepAliveID(g
.ActiveId
);
3639 IM_ASSERT(g
.MovingWindow
&& g
.MovingWindow
->RootWindow
);
3640 ImGuiWindow
* moving_window
= g
.MovingWindow
->RootWindow
;
3641 if (g
.IO
.MouseDown
[0] && IsMousePosValid(&g
.IO
.MousePos
))
3643 ImVec2 pos
= g
.IO
.MousePos
- g
.ActiveIdClickOffset
;
3644 if (moving_window
->Pos
.x
!= pos
.x
|| moving_window
->Pos
.y
!= pos
.y
)
3646 MarkIniSettingsDirty(moving_window
);
3647 SetWindowPos(moving_window
, pos
, ImGuiCond_Always
);
3649 FocusWindow(g
.MovingWindow
);
3654 g
.MovingWindow
= NULL
;
3659 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3660 if (g
.ActiveIdWindow
&& g
.ActiveIdWindow
->MoveId
== g
.ActiveId
)
3662 KeepAliveID(g
.ActiveId
);
3663 if (!g
.IO
.MouseDown
[0])
3669 static bool IsWindowActiveAndVisible(ImGuiWindow
* window
)
3671 return (window
->Active
) && (!window
->Hidden
);
3674 static void ImGui::UpdateMouseInputs()
3676 ImGuiContext
& g
= *GImGui
;
3678 // 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
3679 if (IsMousePosValid(&g
.IO
.MousePos
) && IsMousePosValid(&g
.IO
.MousePosPrev
))
3680 g
.IO
.MouseDelta
= g
.IO
.MousePos
- g
.IO
.MousePosPrev
;
3682 g
.IO
.MouseDelta
= ImVec2(0.0f
, 0.0f
);
3683 if (g
.IO
.MouseDelta
.x
!= 0.0f
|| g
.IO
.MouseDelta
.y
!= 0.0f
)
3684 g
.NavDisableMouseHover
= false;
3686 g
.IO
.MousePosPrev
= g
.IO
.MousePos
;
3687 for (int i
= 0; i
< IM_ARRAYSIZE(g
.IO
.MouseDown
); i
++)
3689 g
.IO
.MouseClicked
[i
] = g
.IO
.MouseDown
[i
] && g
.IO
.MouseDownDuration
[i
] < 0.0f
;
3690 g
.IO
.MouseReleased
[i
] = !g
.IO
.MouseDown
[i
] && g
.IO
.MouseDownDuration
[i
] >= 0.0f
;
3691 g
.IO
.MouseDownDurationPrev
[i
] = g
.IO
.MouseDownDuration
[i
];
3692 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
;
3693 g
.IO
.MouseDoubleClicked
[i
] = false;
3694 if (g
.IO
.MouseClicked
[i
])
3696 if ((float)(g
.Time
- g
.IO
.MouseClickedTime
[i
]) < g
.IO
.MouseDoubleClickTime
)
3698 ImVec2 delta_from_click_pos
= IsMousePosValid(&g
.IO
.MousePos
) ? (g
.IO
.MousePos
- g
.IO
.MouseClickedPos
[i
]) : ImVec2(0.0f
, 0.0f
);
3699 if (ImLengthSqr(delta_from_click_pos
) < g
.IO
.MouseDoubleClickMaxDist
* g
.IO
.MouseDoubleClickMaxDist
)
3700 g
.IO
.MouseDoubleClicked
[i
] = true;
3701 g
.IO
.MouseClickedTime
[i
] = -FLT_MAX
; // so the third click isn't turned into a double-click
3705 g
.IO
.MouseClickedTime
[i
] = g
.Time
;
3707 g
.IO
.MouseClickedPos
[i
] = g
.IO
.MousePos
;
3708 g
.IO
.MouseDragMaxDistanceAbs
[i
] = ImVec2(0.0f
, 0.0f
);
3709 g
.IO
.MouseDragMaxDistanceSqr
[i
] = 0.0f
;
3711 else if (g
.IO
.MouseDown
[i
])
3713 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3714 ImVec2 delta_from_click_pos
= IsMousePosValid(&g
.IO
.MousePos
) ? (g
.IO
.MousePos
- g
.IO
.MouseClickedPos
[i
]) : ImVec2(0.0f
, 0.0f
);
3715 g
.IO
.MouseDragMaxDistanceSqr
[i
] = ImMax(g
.IO
.MouseDragMaxDistanceSqr
[i
], ImLengthSqr(delta_from_click_pos
));
3716 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
);
3717 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
);
3719 if (g
.IO
.MouseClicked
[i
]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3720 g
.NavDisableMouseHover
= false;
3724 void ImGui::UpdateMouseWheel()
3726 ImGuiContext
& g
= *GImGui
;
3727 if (!g
.HoveredWindow
|| g
.HoveredWindow
->Collapsed
)
3729 if (g
.IO
.MouseWheel
== 0.0f
&& g
.IO
.MouseWheelH
== 0.0f
)
3732 // 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).
3733 ImGuiWindow
* window
= g
.HoveredWindow
;
3734 ImGuiWindow
* scroll_window
= window
;
3735 while ((scroll_window
->Flags
& ImGuiWindowFlags_ChildWindow
) && (scroll_window
->Flags
& ImGuiWindowFlags_NoScrollWithMouse
) && !(scroll_window
->Flags
& ImGuiWindowFlags_NoScrollbar
) && !(scroll_window
->Flags
& ImGuiWindowFlags_NoInputs
) && scroll_window
->ParentWindow
)
3736 scroll_window
= scroll_window
->ParentWindow
;
3737 const bool scroll_allowed
= !(scroll_window
->Flags
& ImGuiWindowFlags_NoScrollWithMouse
) && !(scroll_window
->Flags
& ImGuiWindowFlags_NoInputs
);
3739 if (g
.IO
.MouseWheel
!= 0.0f
)
3741 if (g
.IO
.KeyCtrl
&& g
.IO
.FontAllowUserScaling
)
3743 // Zoom / Scale window
3744 const float new_font_scale
= ImClamp(window
->FontWindowScale
+ g
.IO
.MouseWheel
* 0.10f
, 0.50f
, 2.50f
);
3745 const float scale
= new_font_scale
/ window
->FontWindowScale
;
3746 window
->FontWindowScale
= new_font_scale
;
3748 const ImVec2 offset
= window
->Size
* (1.0f
- scale
) * (g
.IO
.MousePos
- window
->Pos
) / window
->Size
;
3749 window
->Pos
+= offset
;
3750 window
->Size
*= scale
;
3751 window
->SizeFull
*= scale
;
3753 else if (!g
.IO
.KeyCtrl
&& scroll_allowed
)
3755 // Mouse wheel vertical scrolling
3756 float scroll_amount
= 5 * scroll_window
->CalcFontSize();
3757 scroll_amount
= (float)(int)ImMin(scroll_amount
, (scroll_window
->ContentsRegionRect
.GetHeight() + scroll_window
->WindowPadding
.y
* 2.0f
) * 0.67f
);
3758 SetWindowScrollY(scroll_window
, scroll_window
->Scroll
.y
- g
.IO
.MouseWheel
* scroll_amount
);
3761 if (g
.IO
.MouseWheelH
!= 0.0f
&& scroll_allowed
&& !g
.IO
.KeyCtrl
)
3763 // Mouse wheel horizontal scrolling (for hardware that supports it)
3764 float scroll_amount
= scroll_window
->CalcFontSize();
3765 SetWindowScrollX(scroll_window
, scroll_window
->Scroll
.x
- g
.IO
.MouseWheelH
* scroll_amount
);
3769 // 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)
3770 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3772 ImGuiContext
& g
= *GImGui
;
3774 // Find the window hovered by mouse:
3775 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3776 // - 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.
3777 // - 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.
3778 FindHoveredWindow();
3780 // Modal windows prevents cursor from hovering behind them.
3781 ImGuiWindow
* modal_window
= GetFrontMostPopupModal();
3783 if (g
.HoveredRootWindow
&& !IsWindowChildOf(g
.HoveredRootWindow
, modal_window
))
3784 g
.HoveredRootWindow
= g
.HoveredWindow
= NULL
;
3787 if (g
.IO
.ConfigFlags
& ImGuiConfigFlags_NoMouse
)
3788 g
.HoveredWindow
= g
.HoveredRootWindow
= NULL
;
3790 // 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.
3791 int mouse_earliest_button_down
= -1;
3792 bool mouse_any_down
= false;
3793 for (int i
= 0; i
< IM_ARRAYSIZE(g
.IO
.MouseDown
); i
++)
3795 if (g
.IO
.MouseClicked
[i
])
3796 g
.IO
.MouseDownOwned
[i
] = (g
.HoveredWindow
!= NULL
) || (!g
.OpenPopupStack
.empty());
3797 mouse_any_down
|= g
.IO
.MouseDown
[i
];
3798 if (g
.IO
.MouseDown
[i
])
3799 if (mouse_earliest_button_down
== -1 || g
.IO
.MouseClickedTime
[i
] < g
.IO
.MouseClickedTime
[mouse_earliest_button_down
])
3800 mouse_earliest_button_down
= i
;
3802 const bool mouse_avail_to_imgui
= (mouse_earliest_button_down
== -1) || g
.IO
.MouseDownOwned
[mouse_earliest_button_down
];
3804 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3805 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3806 const bool mouse_dragging_extern_payload
= g
.DragDropActive
&& (g
.DragDropSourceFlags
& ImGuiDragDropFlags_SourceExtern
) != 0;
3807 if (!mouse_avail_to_imgui
&& !mouse_dragging_extern_payload
)
3808 g
.HoveredWindow
= g
.HoveredRootWindow
= NULL
;
3810 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to imgui + app)
3811 if (g
.WantCaptureMouseNextFrame
!= -1)
3812 g
.IO
.WantCaptureMouse
= (g
.WantCaptureMouseNextFrame
!= 0);
3814 g
.IO
.WantCaptureMouse
= (mouse_avail_to_imgui
&& (g
.HoveredWindow
!= NULL
|| mouse_any_down
)) || (!g
.OpenPopupStack
.empty());
3816 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to imgui + app)
3817 if (g
.WantCaptureKeyboardNextFrame
!= -1)
3818 g
.IO
.WantCaptureKeyboard
= (g
.WantCaptureKeyboardNextFrame
!= 0);
3820 g
.IO
.WantCaptureKeyboard
= (g
.ActiveId
!= 0) || (modal_window
!= NULL
);
3821 if (g
.IO
.NavActive
&& (g
.IO
.ConfigFlags
& ImGuiConfigFlags_NavEnableKeyboard
) && !(g
.IO
.ConfigFlags
& ImGuiConfigFlags_NavNoCaptureKeyboard
))
3822 g
.IO
.WantCaptureKeyboard
= true;
3824 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3825 g
.IO
.WantTextInput
= (g
.WantTextInputNextFrame
!= -1) ? (g
.WantTextInputNextFrame
!= 0) : false;
3828 void ImGui::NewFrame()
3830 IM_ASSERT(GImGui
!= NULL
&& "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3831 ImGuiContext
& g
= *GImGui
;
3834 // (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)
3835 IM_ASSERT(g
.Initialized
);
3836 IM_ASSERT(g
.IO
.DeltaTime
>= 0.0f
&& "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)");
3837 IM_ASSERT(g
.IO
.DisplaySize
.x
>= 0.0f
&& g
.IO
.DisplaySize
.y
>= 0.0f
&& "Invalid DisplaySize value");
3838 IM_ASSERT(g
.IO
.Fonts
->Fonts
.Size
> 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3839 IM_ASSERT(g
.IO
.Fonts
->Fonts
[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3840 IM_ASSERT(g
.Style
.CurveTessellationTol
> 0.0f
&& "Invalid style setting");
3841 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)");
3842 IM_ASSERT((g
.FrameCount
== 0 || g
.FrameCountEnded
== g
.FrameCount
) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3843 for (int n
= 0; n
< ImGuiKey_COUNT
; n
++)
3844 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)");
3846 // 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)
3847 if (g
.IO
.ConfigFlags
& ImGuiConfigFlags_NavEnableKeyboard
)
3848 IM_ASSERT(g
.IO
.KeyMap
[ImGuiKey_Space
] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3850 // The beta io.OptResizeWindowsFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
3851 if (g
.IO
.OptResizeWindowsFromEdges
&& !(g
.IO
.BackendFlags
& ImGuiBackendFlags_HasMouseCursors
))
3852 g
.IO
.OptResizeWindowsFromEdges
= false;
3854 // Load settings on first frame (if not explicitly loaded manually before)
3855 if (!g
.SettingsLoaded
)
3857 IM_ASSERT(g
.SettingsWindows
.empty());
3858 if (g
.IO
.IniFilename
)
3859 LoadIniSettingsFromDisk(g
.IO
.IniFilename
);
3860 g
.SettingsLoaded
= true;
3863 // Save settings (with a delay after the last modification, so we don't spam disk too much)
3864 if (g
.SettingsDirtyTimer
> 0.0f
)
3866 g
.SettingsDirtyTimer
-= g
.IO
.DeltaTime
;
3867 if (g
.SettingsDirtyTimer
<= 0.0f
)
3869 if (g
.IO
.IniFilename
!= NULL
)
3870 SaveIniSettingsToDisk(g
.IO
.IniFilename
);
3872 g
.IO
.WantSaveIniSettings
= true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
3873 g
.SettingsDirtyTimer
= 0.0f
;
3877 g
.Time
+= g
.IO
.DeltaTime
;
3879 g
.TooltipOverrideCount
= 0;
3880 g
.WindowsActiveCount
= 0;
3882 // Setup current font and draw list
3883 g
.IO
.Fonts
->Locked
= true;
3884 SetCurrentFont(GetDefaultFont());
3885 IM_ASSERT(g
.Font
->IsLoaded());
3886 g
.DrawListSharedData
.ClipRectFullscreen
= ImVec4(0.0f
, 0.0f
, g
.IO
.DisplaySize
.x
, g
.IO
.DisplaySize
.y
);
3887 g
.DrawListSharedData
.CurveTessellationTol
= g
.Style
.CurveTessellationTol
;
3889 g
.OverlayDrawList
.Clear();
3890 g
.OverlayDrawList
.PushTextureID(g
.IO
.Fonts
->TexID
);
3891 g
.OverlayDrawList
.PushClipRectFullScreen();
3892 g
.OverlayDrawList
.Flags
= (g
.Style
.AntiAliasedLines
? ImDrawListFlags_AntiAliasedLines
: 0) | (g
.Style
.AntiAliasedFill
? ImDrawListFlags_AntiAliasedFill
: 0);
3894 // Mark rendering data as invalid to prevent user who may have a handle on it to use it
3897 // Clear reference to active widget if the widget isn't alive anymore
3898 if (!g
.HoveredIdPreviousFrame
)
3899 g
.HoveredIdTimer
= 0.0f
;
3900 g
.HoveredIdPreviousFrame
= g
.HoveredId
;
3902 g
.HoveredIdAllowOverlap
= false;
3903 if (!g
.ActiveIdIsAlive
&& g
.ActiveIdPreviousFrame
== g
.ActiveId
&& g
.ActiveId
!= 0)
3906 g
.ActiveIdTimer
+= g
.IO
.DeltaTime
;
3907 g
.LastActiveIdTimer
+= g
.IO
.DeltaTime
;
3908 g
.ActiveIdPreviousFrame
= g
.ActiveId
;
3909 g
.ActiveIdPreviousFrameWindow
= g
.ActiveIdWindow
;
3910 g
.ActiveIdPreviousFrameValueChanged
= g
.ActiveIdValueChanged
;
3911 g
.ActiveIdIsAlive
= g
.ActiveIdPreviousFrameIsAlive
= false;
3912 g
.ActiveIdIsJustActivated
= false;
3913 if (g
.ScalarAsInputTextId
&& g
.ActiveId
!= g
.ScalarAsInputTextId
)
3914 g
.ScalarAsInputTextId
= 0;
3916 // Elapse drag & drop payload
3917 if (g
.DragDropActive
&& g
.DragDropPayload
.DataFrameCount
+ 1 < g
.FrameCount
)
3920 g
.DragDropPayloadBufHeap
.clear();
3921 memset(&g
.DragDropPayloadBufLocal
, 0, sizeof(g
.DragDropPayloadBufLocal
));
3923 g
.DragDropAcceptIdPrev
= g
.DragDropAcceptIdCurr
;
3924 g
.DragDropAcceptIdCurr
= 0;
3925 g
.DragDropAcceptIdCurrRectSurface
= FLT_MAX
;
3926 g
.DragDropWithinSourceOrTarget
= false;
3928 // Update keyboard input state
3929 memcpy(g
.IO
.KeysDownDurationPrev
, g
.IO
.KeysDownDuration
, sizeof(g
.IO
.KeysDownDuration
));
3930 for (int i
= 0; i
< IM_ARRAYSIZE(g
.IO
.KeysDown
); i
++)
3931 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
;
3933 // Update gamepad/keyboard directional navigation
3936 // Update mouse input state
3937 UpdateMouseInputs();
3939 // Calculate frame-rate for the user, as a purely luxurious feature
3940 g
.FramerateSecPerFrameAccum
+= g
.IO
.DeltaTime
- g
.FramerateSecPerFrame
[g
.FramerateSecPerFrameIdx
];
3941 g
.FramerateSecPerFrame
[g
.FramerateSecPerFrameIdx
] = g
.IO
.DeltaTime
;
3942 g
.FramerateSecPerFrameIdx
= (g
.FramerateSecPerFrameIdx
+ 1) % IM_ARRAYSIZE(g
.FramerateSecPerFrame
);
3943 g
.IO
.Framerate
= (g
.FramerateSecPerFrameAccum
> 0.0f
) ? (1.0f
/ (g
.FramerateSecPerFrameAccum
/ (float)IM_ARRAYSIZE(g
.FramerateSecPerFrame
))) : FLT_MAX
;
3945 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3946 UpdateMouseMovingWindow();
3947 UpdateHoveredWindowAndCaptureFlags();
3949 // Background darkening/whitening
3950 if (GetFrontMostPopupModal() != NULL
|| (g
.NavWindowingTarget
!= NULL
&& g
.NavWindowingHighlightAlpha
> 0.0f
))
3951 g
.DimBgRatio
= ImMin(g
.DimBgRatio
+ g
.IO
.DeltaTime
* 6.0f
, 1.0f
);
3953 g
.DimBgRatio
= ImMax(g
.DimBgRatio
- g
.IO
.DeltaTime
* 10.0f
, 0.0f
);
3955 g
.MouseCursor
= ImGuiMouseCursor_Arrow
;
3956 g
.WantCaptureMouseNextFrame
= g
.WantCaptureKeyboardNextFrame
= g
.WantTextInputNextFrame
= -1;
3957 g
.PlatformImePos
= ImVec2(1.0f
, 1.0f
); // OS Input Method Editor showing on top-left of our window by default
3959 // Mouse wheel scrolling, scale
3962 // Pressing TAB activate widget focus
3963 if (g
.ActiveId
== 0 && g
.NavWindow
!= NULL
&& g
.NavWindow
->Active
&& !(g
.NavWindow
->Flags
& ImGuiWindowFlags_NoNavInputs
) && !g
.IO
.KeyCtrl
&& IsKeyPressedMap(ImGuiKey_Tab
, false))
3965 if (g
.NavId
!= 0 && g
.NavIdTabCounter
!= INT_MAX
)
3966 g
.NavWindow
->FocusIdxTabRequestNext
= g
.NavIdTabCounter
+ 1 + (g
.IO
.KeyShift
? -1 : 1);
3968 g
.NavWindow
->FocusIdxTabRequestNext
= g
.IO
.KeyShift
? -1 : 0;
3970 g
.NavIdTabCounter
= INT_MAX
;
3972 // Mark all windows as not visible
3973 for (int i
= 0; i
!= g
.Windows
.Size
; i
++)
3975 ImGuiWindow
* window
= g
.Windows
[i
];
3976 window
->WasActive
= window
->Active
;
3977 window
->Active
= false;
3978 window
->WriteAccessed
= false;
3981 // Closing the focused window restore focus to the first active root window in descending z-order
3982 if (g
.NavWindow
&& !g
.NavWindow
->WasActive
)
3983 FocusFrontMostActiveWindow(NULL
);
3985 // No window should be open at the beginning of the frame.
3986 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3987 g
.CurrentWindowStack
.resize(0);
3988 g
.CurrentPopupStack
.resize(0);
3989 ClosePopupsOverWindow(g
.NavWindow
);
3991 // Create implicit window - we will only render it if the user has added something to it.
3992 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3993 SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver
);
3994 Begin("Debug##Default");
3997 static void* SettingsHandlerWindow_ReadOpen(ImGuiContext
*, ImGuiSettingsHandler
*, const char* name
)
3999 ImGuiWindowSettings
* settings
= ImGui::FindWindowSettings(ImHash(name
, 0));
4001 settings
= CreateNewWindowSettings(name
);
4002 return (void*)settings
;
4005 static void SettingsHandlerWindow_ReadLine(ImGuiContext
*, ImGuiSettingsHandler
*, void* entry
, const char* line
)
4007 ImGuiWindowSettings
* settings
= (ImGuiWindowSettings
*)entry
;
4010 if (sscanf(line
, "Pos=%f,%f", &x
, &y
) == 2) settings
->Pos
= ImVec2(x
, y
);
4011 else if (sscanf(line
, "Size=%f,%f", &x
, &y
) == 2) settings
->Size
= ImMax(ImVec2(x
, y
), GImGui
->Style
.WindowMinSize
);
4012 else if (sscanf(line
, "Collapsed=%d", &i
) == 1) settings
->Collapsed
= (i
!= 0);
4015 static void SettingsHandlerWindow_WriteAll(ImGuiContext
* imgui_ctx
, ImGuiSettingsHandler
* handler
, ImGuiTextBuffer
* buf
)
4017 // Gather data from windows that were active during this session
4018 ImGuiContext
& g
= *imgui_ctx
;
4019 for (int i
= 0; i
!= g
.Windows
.Size
; i
++)
4021 ImGuiWindow
* window
= g
.Windows
[i
];
4022 if (window
->Flags
& ImGuiWindowFlags_NoSavedSettings
)
4025 ImGuiWindowSettings
* settings
= (window
->SettingsIdx
!= -1) ? &g
.SettingsWindows
[window
->SettingsIdx
] : ImGui::FindWindowSettings(window
->ID
);
4028 settings
= CreateNewWindowSettings(window
->Name
);
4029 window
->SettingsIdx
= g
.SettingsWindows
.index_from_pointer(settings
);
4031 IM_ASSERT(settings
->ID
== window
->ID
);
4032 settings
->Pos
= window
->Pos
;
4033 settings
->Size
= window
->SizeFull
;
4034 settings
->Collapsed
= window
->Collapsed
;
4038 // If a window wasn't opened in this session we preserve its settings
4039 buf
->reserve(buf
->size() + g
.SettingsWindows
.Size
* 96); // ballpark reserve
4040 for (int i
= 0; i
!= g
.SettingsWindows
.Size
; i
++)
4042 const ImGuiWindowSettings
* settings
= &g
.SettingsWindows
[i
];
4043 if (settings
->Pos
.x
== FLT_MAX
)
4045 const char* name
= settings
->Name
;
4046 if (const char* p
= strstr(name
, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
4048 buf
->appendf("[%s][%s]\n", handler
->TypeName
, name
);
4049 buf
->appendf("Pos=%d,%d\n", (int)settings
->Pos
.x
, (int)settings
->Pos
.y
);
4050 buf
->appendf("Size=%d,%d\n", (int)settings
->Size
.x
, (int)settings
->Size
.y
);
4051 buf
->appendf("Collapsed=%d\n", settings
->Collapsed
);
4056 void ImGui::Initialize(ImGuiContext
* context
)
4058 ImGuiContext
& g
= *context
;
4059 IM_ASSERT(!g
.Initialized
&& !g
.SettingsLoaded
);
4061 // Add .ini handle for ImGuiWindow type
4062 ImGuiSettingsHandler ini_handler
;
4063 ini_handler
.TypeName
= "Window";
4064 ini_handler
.TypeHash
= ImHash("Window", 0, 0);
4065 ini_handler
.ReadOpenFn
= SettingsHandlerWindow_ReadOpen
;
4066 ini_handler
.ReadLineFn
= SettingsHandlerWindow_ReadLine
;
4067 ini_handler
.WriteAllFn
= SettingsHandlerWindow_WriteAll
;
4068 g
.SettingsHandlers
.push_front(ini_handler
);
4070 g
.Initialized
= true;
4073 // This function is merely here to free heap allocations.
4074 void ImGui::Shutdown(ImGuiContext
* context
)
4076 // 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)
4077 ImGuiContext
& g
= *context
;
4078 if (g
.IO
.Fonts
&& g
.FontAtlasOwnedByContext
)
4079 IM_DELETE(g
.IO
.Fonts
);
4082 // Cleanup of other data are conditional on actually having initialized ImGui.
4086 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
4087 if (g
.SettingsLoaded
&& g
.IO
.IniFilename
!= NULL
)
4088 SaveIniSettingsToDisk(g
.IO
.IniFilename
);
4090 // Clear everything else
4091 for (int i
= 0; i
< g
.Windows
.Size
; i
++)
4092 IM_DELETE(g
.Windows
[i
]);
4094 g
.WindowsSortBuffer
.clear();
4095 g
.CurrentWindow
= NULL
;
4096 g
.CurrentWindowStack
.clear();
4097 g
.WindowsById
.Clear();
4099 g
.HoveredWindow
= NULL
;
4100 g
.HoveredRootWindow
= NULL
;
4101 g
.ActiveIdWindow
= g
.ActiveIdPreviousFrameWindow
= NULL
;
4102 g
.MovingWindow
= NULL
;
4103 g
.ColorModifiers
.clear();
4104 g
.StyleModifiers
.clear();
4105 g
.FontStack
.clear();
4106 g
.OpenPopupStack
.clear();
4107 g
.CurrentPopupStack
.clear();
4108 g
.DrawDataBuilder
.ClearFreeMemory();
4109 g
.OverlayDrawList
.ClearFreeMemory();
4110 g
.PrivateClipboard
.clear();
4111 g
.InputTextState
.Text
.clear();
4112 g
.InputTextState
.InitialText
.clear();
4113 g
.InputTextState
.TempTextBuffer
.clear();
4115 for (int i
= 0; i
< g
.SettingsWindows
.Size
; i
++)
4116 IM_DELETE(g
.SettingsWindows
[i
].Name
);
4117 g
.SettingsWindows
.clear();
4118 g
.SettingsHandlers
.clear();
4120 if (g
.LogFile
&& g
.LogFile
!= stdout
)
4125 g
.LogClipboard
.clear();
4127 g
.Initialized
= false;
4130 ImGuiWindowSettings
* ImGui::FindWindowSettings(ImGuiID id
)
4132 ImGuiContext
& g
= *GImGui
;
4133 for (int i
= 0; i
!= g
.SettingsWindows
.Size
; i
++)
4134 if (g
.SettingsWindows
[i
].ID
== id
)
4135 return &g
.SettingsWindows
[i
];
4139 static ImGuiWindowSettings
* CreateNewWindowSettings(const char* name
)
4141 ImGuiContext
& g
= *GImGui
;
4142 g
.SettingsWindows
.push_back(ImGuiWindowSettings());
4143 ImGuiWindowSettings
* settings
= &g
.SettingsWindows
.back();
4144 settings
->Name
= ImStrdup(name
);
4145 settings
->ID
= ImHash(name
, 0);
4149 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename
)
4151 size_t file_data_size
= 0;
4152 char* file_data
= (char*)ImFileLoadToMemory(ini_filename
, "rb", &file_data_size
);
4155 LoadIniSettingsFromMemory(file_data
, (size_t)file_data_size
);
4156 ImGui::MemFree(file_data
);
4159 ImGuiSettingsHandler
* ImGui::FindSettingsHandler(const char* type_name
)
4161 ImGuiContext
& g
= *GImGui
;
4162 const ImGuiID type_hash
= ImHash(type_name
, 0, 0);
4163 for (int handler_n
= 0; handler_n
< g
.SettingsHandlers
.Size
; handler_n
++)
4164 if (g
.SettingsHandlers
[handler_n
].TypeHash
== type_hash
)
4165 return &g
.SettingsHandlers
[handler_n
];
4169 // Zero-tolerance, no error reporting, cheap .ini parsing
4170 void ImGui::LoadIniSettingsFromMemory(const char* ini_data
, size_t ini_size
)
4172 ImGuiContext
& g
= *GImGui
;
4173 IM_ASSERT(g
.Initialized
);
4174 IM_ASSERT(g
.SettingsLoaded
== false && g
.FrameCount
== 0);
4176 // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
4177 // 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..
4179 ini_size
= strlen(ini_data
);
4180 char* buf
= (char*)ImGui::MemAlloc(ini_size
+ 1);
4181 char* buf_end
= buf
+ ini_size
;
4182 memcpy(buf
, ini_data
, ini_size
);
4185 void* entry_data
= NULL
;
4186 ImGuiSettingsHandler
* entry_handler
= NULL
;
4188 char* line_end
= NULL
;
4189 for (char* line
= buf
; line
< buf_end
; line
= line_end
+ 1)
4191 // Skip new lines markers, then find end of the line
4192 while (*line
== '\n' || *line
== '\r')
4195 while (line_end
< buf_end
&& *line_end
!= '\n' && *line_end
!= '\r')
4199 if (line
[0] == '[' && line_end
> line
&& line_end
[-1] == ']')
4201 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
4203 const char* name_end
= line_end
- 1;
4204 const char* type_start
= line
+ 1;
4205 char* type_end
= (char*)(intptr_t)ImStrchrRange(type_start
, name_end
, ']');
4206 const char* name_start
= type_end
? ImStrchrRange(type_end
+ 1, name_end
, '[') : NULL
;
4207 if (!type_end
|| !name_start
)
4209 name_start
= type_start
; // Import legacy entries that have no type
4210 type_start
= "Window";
4214 *type_end
= 0; // Overwrite first ']'
4215 name_start
++; // Skip second '['
4217 entry_handler
= FindSettingsHandler(type_start
);
4218 entry_data
= entry_handler
? entry_handler
->ReadOpenFn(&g
, entry_handler
, name_start
) : NULL
;
4220 else if (entry_handler
!= NULL
&& entry_data
!= NULL
)
4222 // Let type handler parse the line
4223 entry_handler
->ReadLineFn(&g
, entry_handler
, entry_data
, line
);
4226 ImGui::MemFree(buf
);
4227 g
.SettingsLoaded
= true;
4230 void ImGui::SaveIniSettingsToDisk(const char* ini_filename
)
4232 ImGuiContext
& g
= *GImGui
;
4233 g
.SettingsDirtyTimer
= 0.0f
;
4237 size_t ini_data_size
= 0;
4238 const char* ini_data
= SaveIniSettingsToMemory(&ini_data_size
);
4239 FILE* f
= ImFileOpen(ini_filename
, "wt");
4242 fwrite(ini_data
, sizeof(char), ini_data_size
, f
);
4246 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
4247 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size
)
4249 ImGuiContext
& g
= *GImGui
;
4250 g
.SettingsDirtyTimer
= 0.0f
;
4251 g
.SettingsIniData
.Buf
.resize(0);
4252 g
.SettingsIniData
.Buf
.push_back(0);
4253 for (int handler_n
= 0; handler_n
< g
.SettingsHandlers
.Size
; handler_n
++)
4255 ImGuiSettingsHandler
* handler
= &g
.SettingsHandlers
[handler_n
];
4256 handler
->WriteAllFn(&g
, handler
, &g
.SettingsIniData
);
4259 *out_size
= (size_t)g
.SettingsIniData
.size();
4260 return g
.SettingsIniData
.c_str();
4263 void ImGui::MarkIniSettingsDirty()
4265 ImGuiContext
& g
= *GImGui
;
4266 if (g
.SettingsDirtyTimer
<= 0.0f
)
4267 g
.SettingsDirtyTimer
= g
.IO
.IniSavingRate
;
4270 void ImGui::MarkIniSettingsDirty(ImGuiWindow
* window
)
4272 ImGuiContext
& g
= *GImGui
;
4273 if (!(window
->Flags
& ImGuiWindowFlags_NoSavedSettings
))
4274 if (g
.SettingsDirtyTimer
<= 0.0f
)
4275 g
.SettingsDirtyTimer
= g
.IO
.IniSavingRate
;
4278 // FIXME: Add a more explicit sort order in the window structure.
4279 static int IMGUI_CDECL
ChildWindowComparer(const void* lhs
, const void* rhs
)
4281 const ImGuiWindow
* const a
= *(const ImGuiWindow
* const *)lhs
;
4282 const ImGuiWindow
* const b
= *(const ImGuiWindow
* const *)rhs
;
4283 if (int d
= (a
->Flags
& ImGuiWindowFlags_Popup
) - (b
->Flags
& ImGuiWindowFlags_Popup
))
4285 if (int d
= (a
->Flags
& ImGuiWindowFlags_Tooltip
) - (b
->Flags
& ImGuiWindowFlags_Tooltip
))
4287 return (a
->BeginOrderWithinParent
- b
->BeginOrderWithinParent
);
4290 static void AddWindowToSortedBuffer(ImVector
<ImGuiWindow
*>* out_sorted_windows
, ImGuiWindow
* window
)
4292 out_sorted_windows
->push_back(window
);
4295 int count
= window
->DC
.ChildWindows
.Size
;
4297 ImQsort(window
->DC
.ChildWindows
.begin(), (size_t)count
, sizeof(ImGuiWindow
*), ChildWindowComparer
);
4298 for (int i
= 0; i
< count
; i
++)
4300 ImGuiWindow
* child
= window
->DC
.ChildWindows
[i
];
4302 AddWindowToSortedBuffer(out_sorted_windows
, child
);
4307 static void AddDrawListToDrawData(ImVector
<ImDrawList
*>* out_list
, ImDrawList
* draw_list
)
4309 if (draw_list
->CmdBuffer
.empty())
4312 // Remove trailing command if unused
4313 ImDrawCmd
& last_cmd
= draw_list
->CmdBuffer
.back();
4314 if (last_cmd
.ElemCount
== 0 && last_cmd
.UserCallback
== NULL
)
4316 draw_list
->CmdBuffer
.pop_back();
4317 if (draw_list
->CmdBuffer
.empty())
4321 // 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.
4322 IM_ASSERT(draw_list
->VtxBuffer
.Size
== 0 || draw_list
->_VtxWritePtr
== draw_list
->VtxBuffer
.Data
+ draw_list
->VtxBuffer
.Size
);
4323 IM_ASSERT(draw_list
->IdxBuffer
.Size
== 0 || draw_list
->_IdxWritePtr
== draw_list
->IdxBuffer
.Data
+ draw_list
->IdxBuffer
.Size
);
4324 IM_ASSERT((int)draw_list
->_VtxCurrentIdx
== draw_list
->VtxBuffer
.Size
);
4326 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
4327 // If this assert triggers because you are drawing lots of stuff manually:
4328 // 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.
4329 // 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.
4330 // 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:
4331 // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
4332 // 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.
4333 // 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.
4334 if (sizeof(ImDrawIdx
) == 2)
4335 IM_ASSERT(draw_list
->_VtxCurrentIdx
< (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
4337 out_list
->push_back(draw_list
);
4340 static void AddWindowToDrawData(ImVector
<ImDrawList
*>* out_render_list
, ImGuiWindow
* window
)
4342 AddDrawListToDrawData(out_render_list
, window
->DrawList
);
4343 for (int i
= 0; i
< window
->DC
.ChildWindows
.Size
; i
++)
4345 ImGuiWindow
* child
= window
->DC
.ChildWindows
[i
];
4346 if (IsWindowActiveAndVisible(child
)) // clipped children may have been marked not active
4347 AddWindowToDrawData(out_render_list
, child
);
4351 static void AddWindowToDrawDataSelectLayer(ImGuiWindow
* window
)
4353 ImGuiContext
& g
= *GImGui
;
4354 g
.IO
.MetricsActiveWindows
++;
4355 if (window
->Flags
& ImGuiWindowFlags_Tooltip
)
4356 AddWindowToDrawData(&g
.DrawDataBuilder
.Layers
[1], window
);
4358 AddWindowToDrawData(&g
.DrawDataBuilder
.Layers
[0], window
);
4361 void ImDrawDataBuilder::FlattenIntoSingleLayer()
4363 int n
= Layers
[0].Size
;
4365 for (int i
= 1; i
< IM_ARRAYSIZE(Layers
); i
++)
4366 size
+= Layers
[i
].Size
;
4367 Layers
[0].resize(size
);
4368 for (int layer_n
= 1; layer_n
< IM_ARRAYSIZE(Layers
); layer_n
++)
4370 ImVector
<ImDrawList
*>& layer
= Layers
[layer_n
];
4373 memcpy(&Layers
[0][n
], &layer
[0], layer
.Size
* sizeof(ImDrawList
*));
4379 static void SetupDrawData(ImVector
<ImDrawList
*>* draw_lists
, ImDrawData
* out_draw_data
)
4381 ImGuiIO
& io
= ImGui::GetIO();
4382 out_draw_data
->Valid
= true;
4383 out_draw_data
->CmdLists
= (draw_lists
->Size
> 0) ? draw_lists
->Data
: NULL
;
4384 out_draw_data
->CmdListsCount
= draw_lists
->Size
;
4385 out_draw_data
->TotalVtxCount
= out_draw_data
->TotalIdxCount
= 0;
4386 out_draw_data
->DisplayPos
= ImVec2(0.0f
, 0.0f
);
4387 out_draw_data
->DisplaySize
= io
.DisplaySize
;
4388 for (int n
= 0; n
< draw_lists
->Size
; n
++)
4390 out_draw_data
->TotalVtxCount
+= draw_lists
->Data
[n
]->VtxBuffer
.Size
;
4391 out_draw_data
->TotalIdxCount
+= draw_lists
->Data
[n
]->IdxBuffer
.Size
;
4395 // 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.
4396 void ImGui::PushClipRect(const ImVec2
& clip_rect_min
, const ImVec2
& clip_rect_max
, bool intersect_with_current_clip_rect
)
4398 ImGuiWindow
* window
= GetCurrentWindow();
4399 window
->DrawList
->PushClipRect(clip_rect_min
, clip_rect_max
, intersect_with_current_clip_rect
);
4400 window
->ClipRect
= window
->DrawList
->_ClipRectStack
.back();
4403 void ImGui::PopClipRect()
4405 ImGuiWindow
* window
= GetCurrentWindow();
4406 window
->DrawList
->PopClipRect();
4407 window
->ClipRect
= window
->DrawList
->_ClipRectStack
.back();
4410 // This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal.
4411 void ImGui::EndFrame()
4413 ImGuiContext
& g
= *GImGui
;
4414 IM_ASSERT(g
.Initialized
); // Forgot to call ImGui::NewFrame()
4415 if (g
.FrameCountEnded
== g
.FrameCount
) // Don't process EndFrame() multiple times.
4418 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
4419 if (g
.IO
.ImeSetInputScreenPosFn
&& ImLengthSqr(g
.PlatformImeLastPos
- g
.PlatformImePos
) > 0.0001f
)
4421 g
.IO
.ImeSetInputScreenPosFn((int)g
.PlatformImePos
.x
, (int)g
.PlatformImePos
.y
);
4422 g
.PlatformImeLastPos
= g
.PlatformImePos
;
4425 // Hide implicit "Debug" window if it hasn't been used
4426 IM_ASSERT(g
.CurrentWindowStack
.Size
== 1); // Mismatched Begin()/End() calls, did you forget to call end on g.CurrentWindow->Name?
4427 if (g
.CurrentWindow
&& !g
.CurrentWindow
->WriteAccessed
)
4428 g
.CurrentWindow
->Active
= false;
4431 // Show CTRL+TAB list
4432 if (g
.NavWindowingTarget
)
4433 NavUpdateWindowingList();
4435 // Initiate moving window
4436 if (g
.ActiveId
== 0 && g
.HoveredId
== 0)
4438 if (!g
.NavWindow
|| !g
.NavWindow
->Appearing
) // Unless we just made a window/popup appear
4440 // Click to focus window and start moving (after we're done with all our widgets)
4441 if (g
.IO
.MouseClicked
[0])
4443 if (g
.HoveredRootWindow
!= NULL
)
4444 StartMouseMovingWindow(g
.HoveredWindow
);
4445 else if (g
.NavWindow
!= NULL
&& GetFrontMostPopupModal() == NULL
)
4446 FocusWindow(NULL
); // Clicking on void disable focus
4449 // With right mouse button we close popups without changing focus
4450 // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger)
4451 if (g
.IO
.MouseClicked
[1])
4453 // Find the top-most window between HoveredWindow and the front most Modal Window.
4454 // This is where we can trim the popup stack.
4455 ImGuiWindow
* modal
= GetFrontMostPopupModal();
4456 bool hovered_window_above_modal
= false;
4458 hovered_window_above_modal
= true;
4459 for (int i
= g
.Windows
.Size
- 1; i
>= 0 && hovered_window_above_modal
== false; i
--)
4461 ImGuiWindow
* window
= g
.Windows
[i
];
4462 if (window
== modal
)
4464 if (window
== g
.HoveredWindow
)
4465 hovered_window_above_modal
= true;
4467 ClosePopupsOverWindow(hovered_window_above_modal
? g
.HoveredWindow
: modal
);
4472 // Sort the window list so that all child windows are after their parent
4473 // We cannot do that on FocusWindow() because childs may not exist yet
4474 g
.WindowsSortBuffer
.resize(0);
4475 g
.WindowsSortBuffer
.reserve(g
.Windows
.Size
);
4476 for (int i
= 0; i
!= g
.Windows
.Size
; i
++)
4478 ImGuiWindow
* window
= g
.Windows
[i
];
4479 if (window
->Active
&& (window
->Flags
& ImGuiWindowFlags_ChildWindow
)) // if a child is active its parent will add it
4481 AddWindowToSortedBuffer(&g
.WindowsSortBuffer
, window
);
4484 IM_ASSERT(g
.Windows
.Size
== g
.WindowsSortBuffer
.Size
); // we done something wrong
4485 g
.Windows
.swap(g
.WindowsSortBuffer
);
4487 // Unlock font atlas
4488 g
.IO
.Fonts
->Locked
= false;
4490 // Clear Input data for next frame
4491 g
.IO
.MouseWheel
= g
.IO
.MouseWheelH
= 0.0f
;
4492 memset(g
.IO
.InputCharacters
, 0, sizeof(g
.IO
.InputCharacters
));
4493 memset(g
.IO
.NavInputs
, 0, sizeof(g
.IO
.NavInputs
));
4495 g
.FrameCountEnded
= g
.FrameCount
;
4498 void ImGui::Render()
4500 ImGuiContext
& g
= *GImGui
;
4501 IM_ASSERT(g
.Initialized
); // Forgot to call ImGui::NewFrame()
4503 if (g
.FrameCountEnded
!= g
.FrameCount
)
4505 g
.FrameCountRendered
= g
.FrameCount
;
4507 // Gather windows to render
4508 g
.IO
.MetricsRenderVertices
= g
.IO
.MetricsRenderIndices
= g
.IO
.MetricsActiveWindows
= 0;
4509 g
.DrawDataBuilder
.Clear();
4510 ImGuiWindow
* windows_to_render_front_most
[2];
4511 windows_to_render_front_most
[0] = (g
.NavWindowingTarget
&& !(g
.NavWindowingTarget
->Flags
& ImGuiWindowFlags_NoBringToFrontOnFocus
)) ? g
.NavWindowingTarget
->RootWindow
: NULL
;
4512 windows_to_render_front_most
[1] = g
.NavWindowingTarget
? g
.NavWindowingList
: NULL
;
4513 for (int n
= 0; n
!= g
.Windows
.Size
; n
++)
4515 ImGuiWindow
* window
= g
.Windows
[n
];
4516 if (IsWindowActiveAndVisible(window
) && (window
->Flags
& ImGuiWindowFlags_ChildWindow
) == 0 && window
!= windows_to_render_front_most
[0] && window
!= windows_to_render_front_most
[1])
4517 AddWindowToDrawDataSelectLayer(window
);
4519 for (int n
= 0; n
< IM_ARRAYSIZE(windows_to_render_front_most
); n
++)
4520 if (windows_to_render_front_most
[n
] && IsWindowActiveAndVisible(windows_to_render_front_most
[n
])) // NavWindowingTarget is always temporarily displayed as the front-most window
4521 AddWindowToDrawDataSelectLayer(windows_to_render_front_most
[n
]);
4522 g
.DrawDataBuilder
.FlattenIntoSingleLayer();
4524 // Draw software mouse cursor if requested
4525 ImVec2 offset
, size
, uv
[4];
4526 if (g
.IO
.MouseDrawCursor
&& g
.IO
.Fonts
->GetMouseCursorTexData(g
.MouseCursor
, &offset
, &size
, &uv
[0], &uv
[2]))
4528 const ImVec2 pos
= g
.IO
.MousePos
- offset
;
4529 const ImTextureID tex_id
= g
.IO
.Fonts
->TexID
;
4530 const float sc
= g
.Style
.MouseCursorScale
;
4531 g
.OverlayDrawList
.PushTextureID(tex_id
);
4532 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
4533 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
4534 g
.OverlayDrawList
.AddImage(tex_id
, pos
, pos
+ size
*sc
, uv
[2], uv
[3], IM_COL32(0,0,0,255)); // Black border
4535 g
.OverlayDrawList
.AddImage(tex_id
, pos
, pos
+ size
*sc
, uv
[0], uv
[1], IM_COL32(255,255,255,255)); // White fill
4536 g
.OverlayDrawList
.PopTextureID();
4538 if (!g
.OverlayDrawList
.VtxBuffer
.empty())
4539 AddDrawListToDrawData(&g
.DrawDataBuilder
.Layers
[0], &g
.OverlayDrawList
);
4541 // Setup ImDrawData structure for end-user
4542 SetupDrawData(&g
.DrawDataBuilder
.Layers
[0], &g
.DrawData
);
4543 g
.IO
.MetricsRenderVertices
= g
.DrawData
.TotalVtxCount
;
4544 g
.IO
.MetricsRenderIndices
= g
.DrawData
.TotalIdxCount
;
4546 // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData()
4547 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4548 if (g
.DrawData
.CmdListsCount
> 0 && g
.IO
.RenderDrawListsFn
!= NULL
)
4549 g
.IO
.RenderDrawListsFn(&g
.DrawData
);
4553 const char* ImGui::FindRenderedTextEnd(const char* text
, const char* text_end
)
4555 const char* text_display_end
= text
;
4557 text_end
= (const char*)-1;
4559 while (text_display_end
< text_end
&& *text_display_end
!= '\0' && (text_display_end
[0] != '#' || text_display_end
[1] != '#'))
4561 return text_display_end
;
4564 // Pass text data straight to log (without being displayed)
4565 void ImGui::LogText(const char* fmt
, ...)
4567 ImGuiContext
& g
= *GImGui
;
4572 va_start(args
, fmt
);
4574 vfprintf(g
.LogFile
, fmt
, args
);
4576 g
.LogClipboard
.appendfv(fmt
, args
);
4580 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
4581 // We split text into individual lines to add current tree level padding
4582 static void LogRenderedText(const ImVec2
* ref_pos
, const char* text
, const char* text_end
= NULL
)
4584 ImGuiContext
& g
= *GImGui
;
4585 ImGuiWindow
* window
= g
.CurrentWindow
;
4588 text_end
= ImGui::FindRenderedTextEnd(text
, text_end
);
4590 const bool log_new_line
= ref_pos
&& (ref_pos
->y
> window
->DC
.LogLinePosY
+ 1);
4592 window
->DC
.LogLinePosY
= ref_pos
->y
;
4594 const char* text_remaining
= text
;
4595 if (g
.LogStartDepth
> window
->DC
.TreeDepth
) // Re-adjust padding if we have popped out of our starting depth
4596 g
.LogStartDepth
= window
->DC
.TreeDepth
;
4597 const int tree_depth
= (window
->DC
.TreeDepth
- g
.LogStartDepth
);
4600 // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
4601 const char* line_end
= text_remaining
;
4602 while (line_end
< text_end
)
4603 if (*line_end
== '\n')
4607 if (line_end
>= text_end
)
4610 const bool is_first_line
= (text
== text_remaining
);
4611 bool is_last_line
= false;
4612 if (line_end
== NULL
)
4614 is_last_line
= true;
4615 line_end
= text_end
;
4617 if (line_end
!= NULL
&& !(is_last_line
&& (line_end
- text_remaining
)==0))
4619 const int char_count
= (int)(line_end
- text_remaining
);
4620 if (log_new_line
|| !is_first_line
)
4621 ImGui::LogText(IM_NEWLINE
"%*s%.*s", tree_depth
*4, "", char_count
, text_remaining
);
4623 ImGui::LogText(" %.*s", char_count
, text_remaining
);
4628 text_remaining
= line_end
+ 1;
4632 // Internal ImGui functions to render text
4633 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
4634 void ImGui::RenderText(ImVec2 pos
, const char* text
, const char* text_end
, bool hide_text_after_hash
)
4636 ImGuiContext
& g
= *GImGui
;
4637 ImGuiWindow
* window
= g
.CurrentWindow
;
4639 // Hide anything after a '##' string
4640 const char* text_display_end
;
4641 if (hide_text_after_hash
)
4643 text_display_end
= FindRenderedTextEnd(text
, text_end
);
4648 text_end
= text
+ strlen(text
); // FIXME-OPT
4649 text_display_end
= text_end
;
4652 if (text
!= text_display_end
)
4654 window
->DrawList
->AddText(g
.Font
, g
.FontSize
, pos
, GetColorU32(ImGuiCol_Text
), text
, text_display_end
);
4656 LogRenderedText(&pos
, text
, text_display_end
);
4660 void ImGui::RenderTextWrapped(ImVec2 pos
, const char* text
, const char* text_end
, float wrap_width
)
4662 ImGuiContext
& g
= *GImGui
;
4663 ImGuiWindow
* window
= g
.CurrentWindow
;
4666 text_end
= text
+ strlen(text
); // FIXME-OPT
4668 if (text
!= text_end
)
4670 window
->DrawList
->AddText(g
.Font
, g
.FontSize
, pos
, GetColorU32(ImGuiCol_Text
), text
, text_end
, wrap_width
);
4672 LogRenderedText(&pos
, text
, text_end
);
4676 // Default clip_rect uses (pos_min,pos_max)
4677 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
4678 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
)
4680 // Hide anything after a '##' string
4681 const char* text_display_end
= FindRenderedTextEnd(text
, text_end
);
4682 const int text_len
= (int)(text_display_end
- text
);
4686 ImGuiContext
& g
= *GImGui
;
4687 ImGuiWindow
* window
= g
.CurrentWindow
;
4689 // Perform CPU side clipping for single clipped element to avoid using scissor state
4690 ImVec2 pos
= pos_min
;
4691 const ImVec2 text_size
= text_size_if_known
? *text_size_if_known
: CalcTextSize(text
, text_display_end
, false, 0.0f
);
4693 const ImVec2
* clip_min
= clip_rect
? &clip_rect
->Min
: &pos_min
;
4694 const ImVec2
* clip_max
= clip_rect
? &clip_rect
->Max
: &pos_max
;
4695 bool need_clipping
= (pos
.x
+ text_size
.x
>= clip_max
->x
) || (pos
.y
+ text_size
.y
>= clip_max
->y
);
4696 if (clip_rect
) // If we had no explicit clipping rectangle then pos==clip_min
4697 need_clipping
|= (pos
.x
< clip_min
->x
) || (pos
.y
< clip_min
->y
);
4699 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
4700 if (align
.x
> 0.0f
) pos
.x
= ImMax(pos
.x
, pos
.x
+ (pos_max
.x
- pos
.x
- text_size
.x
) * align
.x
);
4701 if (align
.y
> 0.0f
) pos
.y
= ImMax(pos
.y
, pos
.y
+ (pos_max
.y
- pos
.y
- text_size
.y
) * align
.y
);
4706 ImVec4
fine_clip_rect(clip_min
->x
, clip_min
->y
, clip_max
->x
, clip_max
->y
);
4707 window
->DrawList
->AddText(g
.Font
, g
.FontSize
, pos
, GetColorU32(ImGuiCol_Text
), text
, text_display_end
, 0.0f
, &fine_clip_rect
);
4711 window
->DrawList
->AddText(g
.Font
, g
.FontSize
, pos
, GetColorU32(ImGuiCol_Text
), text
, text_display_end
, 0.0f
, NULL
);
4714 LogRenderedText(&pos
, text
, text_display_end
);
4717 // Render a rectangle shaped with optional rounding and borders
4718 void ImGui::RenderFrame(ImVec2 p_min
, ImVec2 p_max
, ImU32 fill_col
, bool border
, float rounding
)
4720 ImGuiContext
& g
= *GImGui
;
4721 ImGuiWindow
* window
= g
.CurrentWindow
;
4722 window
->DrawList
->AddRectFilled(p_min
, p_max
, fill_col
, rounding
);
4723 const float border_size
= g
.Style
.FrameBorderSize
;
4724 if (border
&& border_size
> 0.0f
)
4726 window
->DrawList
->AddRect(p_min
+ImVec2(1,1), p_max
+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow
), rounding
, ImDrawCornerFlags_All
, border_size
);
4727 window
->DrawList
->AddRect(p_min
, p_max
, GetColorU32(ImGuiCol_Border
), rounding
, ImDrawCornerFlags_All
, border_size
);
4731 void ImGui::RenderFrameBorder(ImVec2 p_min
, ImVec2 p_max
, float rounding
)
4733 ImGuiContext
& g
= *GImGui
;
4734 ImGuiWindow
* window
= g
.CurrentWindow
;
4735 const float border_size
= g
.Style
.FrameBorderSize
;
4736 if (border_size
> 0.0f
)
4738 window
->DrawList
->AddRect(p_min
+ImVec2(1,1), p_max
+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow
), rounding
, ImDrawCornerFlags_All
, border_size
);
4739 window
->DrawList
->AddRect(p_min
, p_max
, GetColorU32(ImGuiCol_Border
), rounding
, ImDrawCornerFlags_All
, border_size
);
4743 // Render an arrow. 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side.
4744 void ImGui::RenderArrowPointingAt(ImDrawList
* draw_list
, ImVec2 pos
, ImVec2 half_sz
, ImGuiDir direction
, ImU32 col
)
4748 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;
4749 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;
4750 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;
4751 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;
4752 case ImGuiDir_None
: case ImGuiDir_COUNT
: break; // Fix warnings
4756 // Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state
4757 void ImGui::RenderArrow(ImVec2 p_min
, ImGuiDir dir
, float scale
)
4759 ImGuiContext
& g
= *GImGui
;
4761 const float h
= g
.FontSize
* 1.00f
;
4762 float r
= h
* 0.40f
* scale
;
4763 ImVec2 center
= p_min
+ ImVec2(h
* 0.50f
, h
* 0.50f
* scale
);
4770 if (dir
== ImGuiDir_Up
) r
= -r
;
4771 a
= ImVec2(+0.000f
,+0.750f
) * r
;
4772 b
= ImVec2(-0.866f
,-0.750f
) * r
;
4773 c
= ImVec2(+0.866f
,-0.750f
) * r
;
4776 case ImGuiDir_Right
:
4777 if (dir
== ImGuiDir_Left
) r
= -r
;
4778 a
= ImVec2(+0.750f
,+0.000f
) * r
;
4779 b
= ImVec2(-0.750f
,+0.866f
) * r
;
4780 c
= ImVec2(-0.750f
,-0.866f
) * r
;
4783 case ImGuiDir_COUNT
:
4788 g
.CurrentWindow
->DrawList
->AddTriangleFilled(center
+ a
, center
+ b
, center
+ c
, GetColorU32(ImGuiCol_Text
));
4791 void ImGui::RenderBullet(ImVec2 pos
)
4793 ImGuiContext
& g
= *GImGui
;
4794 ImGuiWindow
* window
= g
.CurrentWindow
;
4795 window
->DrawList
->AddCircleFilled(pos
, GImGui
->FontSize
*0.20f
, GetColorU32(ImGuiCol_Text
), 8);
4798 void ImGui::RenderCheckMark(ImVec2 pos
, ImU32 col
, float sz
)
4800 ImGuiContext
& g
= *GImGui
;
4801 ImGuiWindow
* window
= g
.CurrentWindow
;
4803 float thickness
= ImMax(sz
/ 5.0f
, 1.0f
);
4804 sz
-= thickness
*0.5f
;
4805 pos
+= ImVec2(thickness
*0.25f
, thickness
*0.25f
);
4807 float third
= sz
/ 3.0f
;
4808 float bx
= pos
.x
+ third
;
4809 float by
= pos
.y
+ sz
- third
*0.5f
;
4810 window
->DrawList
->PathLineTo(ImVec2(bx
- third
, by
- third
));
4811 window
->DrawList
->PathLineTo(ImVec2(bx
, by
));
4812 window
->DrawList
->PathLineTo(ImVec2(bx
+ third
*2, by
- third
*2));
4813 window
->DrawList
->PathStroke(col
, false, thickness
);
4816 void ImGui::RenderNavHighlight(const ImRect
& bb
, ImGuiID id
, ImGuiNavHighlightFlags flags
)
4818 ImGuiContext
& g
= *GImGui
;
4821 if (g
.NavDisableHighlight
&& !(flags
& ImGuiNavHighlightFlags_AlwaysDraw
))
4823 ImGuiWindow
* window
= ImGui::GetCurrentWindow();
4824 if (window
->DC
.NavHideHighlightOneFrame
)
4827 float rounding
= (flags
& ImGuiNavHighlightFlags_NoRounding
) ? 0.0f
: g
.Style
.FrameRounding
;
4828 ImRect display_rect
= bb
;
4829 display_rect
.ClipWith(window
->ClipRect
);
4830 if (flags
& ImGuiNavHighlightFlags_TypeDefault
)
4832 const float THICKNESS
= 2.0f
;
4833 const float DISTANCE
= 3.0f
+ THICKNESS
* 0.5f
;
4834 display_rect
.Expand(ImVec2(DISTANCE
,DISTANCE
));
4835 bool fully_visible
= window
->ClipRect
.Contains(display_rect
);
4837 window
->DrawList
->PushClipRect(display_rect
.Min
, display_rect
.Max
);
4838 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
);
4840 window
->DrawList
->PopClipRect();
4842 if (flags
& ImGuiNavHighlightFlags_TypeThin
)
4844 window
->DrawList
->AddRect(display_rect
.Min
, display_rect
.Max
, GetColorU32(ImGuiCol_NavHighlight
), rounding
, ~0, 1.0f
);
4848 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4849 // CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize)
4850 ImVec2
ImGui::CalcTextSize(const char* text
, const char* text_end
, bool hide_text_after_double_hash
, float wrap_width
)
4852 ImGuiContext
& g
= *GImGui
;
4854 const char* text_display_end
;
4855 if (hide_text_after_double_hash
)
4856 text_display_end
= FindRenderedTextEnd(text
, text_end
); // Hide anything after a '##' string
4858 text_display_end
= text_end
;
4860 ImFont
* font
= g
.Font
;
4861 const float font_size
= g
.FontSize
;
4862 if (text
== text_display_end
)
4863 return ImVec2(0.0f
, font_size
);
4864 ImVec2 text_size
= font
->CalcTextSizeA(font_size
, FLT_MAX
, wrap_width
, text
, text_display_end
, NULL
);
4866 // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field)
4867 const float font_scale
= font_size
/ font
->FontSize
;
4868 const float character_spacing_x
= 1.0f
* font_scale
;
4869 if (text_size
.x
> 0.0f
)
4870 text_size
.x
-= character_spacing_x
;
4871 text_size
.x
= (float)(int)(text_size
.x
+ 0.95f
);
4876 // Helper to calculate coarse clipping of large list of evenly sized items.
4877 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
4878 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
4879 void ImGui::CalcListClipping(int items_count
, float items_height
, int* out_items_display_start
, int* out_items_display_end
)
4881 ImGuiContext
& g
= *GImGui
;
4882 ImGuiWindow
* window
= g
.CurrentWindow
;
4885 // If logging is active, do not perform any clipping
4886 *out_items_display_start
= 0;
4887 *out_items_display_end
= items_count
;
4890 if (window
->SkipItems
)
4892 *out_items_display_start
= *out_items_display_end
= 0;
4896 // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
4897 ImRect unclipped_rect
= window
->ClipRect
;
4898 if (g
.NavMoveRequest
)
4899 unclipped_rect
.Add(g
.NavScoringRectScreen
);
4901 const ImVec2 pos
= window
->DC
.CursorPos
;
4902 int start
= (int)((unclipped_rect
.Min
.y
- pos
.y
) / items_height
);
4903 int end
= (int)((unclipped_rect
.Max
.y
- pos
.y
) / items_height
);
4905 // When performing a navigation request, ensure we have one item extra in the direction we are moving to
4906 if (g
.NavMoveRequest
&& g
.NavMoveClipDir
== ImGuiDir_Up
)
4908 if (g
.NavMoveRequest
&& g
.NavMoveClipDir
== ImGuiDir_Down
)
4911 start
= ImClamp(start
, 0, items_count
);
4912 end
= ImClamp(end
+ 1, start
, items_count
);
4913 *out_items_display_start
= start
;
4914 *out_items_display_end
= end
;
4917 // Find window given position, search front-to-back
4918 // 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.
4919 static void FindHoveredWindow()
4921 ImGuiContext
& g
= *GImGui
;
4923 ImGuiWindow
* hovered_window
= NULL
;
4924 if (g
.MovingWindow
&& !(g
.MovingWindow
->Flags
& ImGuiWindowFlags_NoInputs
))
4925 hovered_window
= g
.MovingWindow
;
4927 for (int i
= g
.Windows
.Size
- 1; i
>= 0 && hovered_window
== NULL
; i
--)
4929 ImGuiWindow
* window
= g
.Windows
[i
];
4930 if (!window
->Active
|| window
->Hidden
)
4932 if (window
->Flags
& ImGuiWindowFlags_NoInputs
)
4935 // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4936 ImRect
bb(window
->OuterRectClipped
.Min
- g
.Style
.TouchExtraPadding
, window
->OuterRectClipped
.Max
+ g
.Style
.TouchExtraPadding
);
4937 if (bb
.Contains(g
.IO
.MousePos
))
4939 if (hovered_window
== NULL
)
4940 hovered_window
= window
;
4946 g
.HoveredWindow
= hovered_window
;
4947 g
.HoveredRootWindow
= g
.HoveredWindow
? g
.HoveredWindow
->RootWindow
: NULL
;
4951 // Test if mouse cursor is hovering given rectangle
4952 // NB- Rectangle is clipped by our current clip setting
4953 // NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
4954 bool ImGui::IsMouseHoveringRect(const ImVec2
& r_min
, const ImVec2
& r_max
, bool clip
)
4956 ImGuiContext
& g
= *GImGui
;
4959 ImRect
rect_clipped(r_min
, r_max
);
4961 rect_clipped
.ClipWith(g
.CurrentWindow
->ClipRect
);
4963 // Expand for touch input
4964 const ImRect
rect_for_touch(rect_clipped
.Min
- g
.Style
.TouchExtraPadding
, rect_clipped
.Max
+ g
.Style
.TouchExtraPadding
);
4965 return rect_for_touch
.Contains(g
.IO
.MousePos
);
4968 static bool IsKeyPressedMap(ImGuiKey key
, bool repeat
)
4970 const int key_index
= GImGui
->IO
.KeyMap
[key
];
4971 return (key_index
>= 0) ? ImGui::IsKeyPressed(key_index
, repeat
) : false;
4974 int ImGui::GetKeyIndex(ImGuiKey imgui_key
)
4976 IM_ASSERT(imgui_key
>= 0 && imgui_key
< ImGuiKey_COUNT
);
4977 return GImGui
->IO
.KeyMap
[imgui_key
];
4980 // 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[]!
4981 bool ImGui::IsKeyDown(int user_key_index
)
4983 if (user_key_index
< 0) return false;
4984 IM_ASSERT(user_key_index
>= 0 && user_key_index
< IM_ARRAYSIZE(GImGui
->IO
.KeysDown
));
4985 return GImGui
->IO
.KeysDown
[user_key_index
];
4988 int ImGui::CalcTypematicPressedRepeatAmount(float t
, float t_prev
, float repeat_delay
, float repeat_rate
)
4992 if (t
<= repeat_delay
|| repeat_rate
<= 0.0f
)
4994 const int count
= (int)((t
- repeat_delay
) / repeat_rate
) - (int)((t_prev
- repeat_delay
) / repeat_rate
);
4995 return (count
> 0) ? count
: 0;
4998 int ImGui::GetKeyPressedAmount(int key_index
, float repeat_delay
, float repeat_rate
)
5000 ImGuiContext
& g
= *GImGui
;
5001 if (key_index
< 0) return false;
5002 IM_ASSERT(key_index
>= 0 && key_index
< IM_ARRAYSIZE(g
.IO
.KeysDown
));
5003 const float t
= g
.IO
.KeysDownDuration
[key_index
];
5004 return CalcTypematicPressedRepeatAmount(t
, t
- g
.IO
.DeltaTime
, repeat_delay
, repeat_rate
);
5007 bool ImGui::IsKeyPressed(int user_key_index
, bool repeat
)
5009 ImGuiContext
& g
= *GImGui
;
5010 if (user_key_index
< 0) return false;
5011 IM_ASSERT(user_key_index
>= 0 && user_key_index
< IM_ARRAYSIZE(g
.IO
.KeysDown
));
5012 const float t
= g
.IO
.KeysDownDuration
[user_key_index
];
5015 if (repeat
&& t
> g
.IO
.KeyRepeatDelay
)
5016 return GetKeyPressedAmount(user_key_index
, g
.IO
.KeyRepeatDelay
, g
.IO
.KeyRepeatRate
) > 0;
5020 bool ImGui::IsKeyReleased(int user_key_index
)
5022 ImGuiContext
& g
= *GImGui
;
5023 if (user_key_index
< 0) return false;
5024 IM_ASSERT(user_key_index
>= 0 && user_key_index
< IM_ARRAYSIZE(g
.IO
.KeysDown
));
5025 return g
.IO
.KeysDownDurationPrev
[user_key_index
] >= 0.0f
&& !g
.IO
.KeysDown
[user_key_index
];
5028 bool ImGui::IsMouseDown(int button
)
5030 ImGuiContext
& g
= *GImGui
;
5031 IM_ASSERT(button
>= 0 && button
< IM_ARRAYSIZE(g
.IO
.MouseDown
));
5032 return g
.IO
.MouseDown
[button
];
5035 bool ImGui::IsAnyMouseDown()
5037 ImGuiContext
& g
= *GImGui
;
5038 for (int n
= 0; n
< IM_ARRAYSIZE(g
.IO
.MouseDown
); n
++)
5039 if (g
.IO
.MouseDown
[n
])
5044 bool ImGui::IsMouseClicked(int button
, bool repeat
)
5046 ImGuiContext
& g
= *GImGui
;
5047 IM_ASSERT(button
>= 0 && button
< IM_ARRAYSIZE(g
.IO
.MouseDown
));
5048 const float t
= g
.IO
.MouseDownDuration
[button
];
5052 if (repeat
&& t
> g
.IO
.KeyRepeatDelay
)
5054 float delay
= g
.IO
.KeyRepeatDelay
, rate
= g
.IO
.KeyRepeatRate
;
5055 if ((ImFmod(t
- delay
, rate
) > rate
*0.5f
) != (ImFmod(t
- delay
- g
.IO
.DeltaTime
, rate
) > rate
*0.5f
))
5062 bool ImGui::IsMouseReleased(int button
)
5064 ImGuiContext
& g
= *GImGui
;
5065 IM_ASSERT(button
>= 0 && button
< IM_ARRAYSIZE(g
.IO
.MouseDown
));
5066 return g
.IO
.MouseReleased
[button
];
5069 bool ImGui::IsMouseDoubleClicked(int button
)
5071 ImGuiContext
& g
= *GImGui
;
5072 IM_ASSERT(button
>= 0 && button
< IM_ARRAYSIZE(g
.IO
.MouseDown
));
5073 return g
.IO
.MouseDoubleClicked
[button
];
5076 bool ImGui::IsMouseDragging(int button
, float lock_threshold
)
5078 ImGuiContext
& g
= *GImGui
;
5079 IM_ASSERT(button
>= 0 && button
< IM_ARRAYSIZE(g
.IO
.MouseDown
));
5080 if (!g
.IO
.MouseDown
[button
])
5082 if (lock_threshold
< 0.0f
)
5083 lock_threshold
= g
.IO
.MouseDragThreshold
;
5084 return g
.IO
.MouseDragMaxDistanceSqr
[button
] >= lock_threshold
* lock_threshold
;
5087 ImVec2
ImGui::GetMousePos()
5089 return GImGui
->IO
.MousePos
;
5092 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
5093 ImVec2
ImGui::GetMousePosOnOpeningCurrentPopup()
5095 ImGuiContext
& g
= *GImGui
;
5096 if (g
.CurrentPopupStack
.Size
> 0)
5097 return g
.OpenPopupStack
[g
.CurrentPopupStack
.Size
-1].OpenMousePos
;
5098 return g
.IO
.MousePos
;
5101 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position
5102 bool ImGui::IsMousePosValid(const ImVec2
* mouse_pos
)
5104 if (mouse_pos
== NULL
)
5105 mouse_pos
= &GImGui
->IO
.MousePos
;
5106 const float MOUSE_INVALID
= -256000.0f
;
5107 return mouse_pos
->x
>= MOUSE_INVALID
&& mouse_pos
->y
>= MOUSE_INVALID
;
5110 // NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window.
5111 ImVec2
ImGui::GetMouseDragDelta(int button
, float lock_threshold
)
5113 ImGuiContext
& g
= *GImGui
;
5114 IM_ASSERT(button
>= 0 && button
< IM_ARRAYSIZE(g
.IO
.MouseDown
));
5115 if (lock_threshold
< 0.0f
)
5116 lock_threshold
= g
.IO
.MouseDragThreshold
;
5117 if (g
.IO
.MouseDown
[button
])
5118 if (g
.IO
.MouseDragMaxDistanceSqr
[button
] >= lock_threshold
* lock_threshold
)
5119 return g
.IO
.MousePos
- g
.IO
.MouseClickedPos
[button
]; // Assume we can only get active with left-mouse button (at the moment).
5120 return ImVec2(0.0f
, 0.0f
);
5123 void ImGui::ResetMouseDragDelta(int button
)
5125 ImGuiContext
& g
= *GImGui
;
5126 IM_ASSERT(button
>= 0 && button
< IM_ARRAYSIZE(g
.IO
.MouseDown
));
5127 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
5128 g
.IO
.MouseClickedPos
[button
] = g
.IO
.MousePos
;
5131 ImGuiMouseCursor
ImGui::GetMouseCursor()
5133 return GImGui
->MouseCursor
;
5136 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type
)
5138 GImGui
->MouseCursor
= cursor_type
;
5141 void ImGui::CaptureKeyboardFromApp(bool capture
)
5143 GImGui
->WantCaptureKeyboardNextFrame
= capture
? 1 : 0;
5146 void ImGui::CaptureMouseFromApp(bool capture
)
5148 GImGui
->WantCaptureMouseNextFrame
= capture
? 1 : 0;
5151 bool ImGui::IsItemActive()
5153 ImGuiContext
& g
= *GImGui
;
5156 ImGuiWindow
* window
= g
.CurrentWindow
;
5157 return g
.ActiveId
== window
->DC
.LastItemId
;
5162 bool ImGui::IsItemDeactivated()
5164 ImGuiContext
& g
= *GImGui
;
5165 ImGuiWindow
* window
= g
.CurrentWindow
;
5166 return (g
.ActiveIdPreviousFrame
== window
->DC
.LastItemId
&& g
.ActiveIdPreviousFrame
!= 0 && g
.ActiveId
!= window
->DC
.LastItemId
);
5169 bool ImGui::IsItemDeactivatedAfterChange()
5171 ImGuiContext
& g
= *GImGui
;
5172 return IsItemDeactivated() && (g
.ActiveIdPreviousFrameValueChanged
|| (g
.ActiveId
== 0 && g
.ActiveIdValueChanged
));
5175 bool ImGui::IsItemFocused()
5177 ImGuiContext
& g
= *GImGui
;
5178 return g
.NavId
&& !g
.NavDisableHighlight
&& g
.NavId
== g
.CurrentWindow
->DC
.LastItemId
;
5181 bool ImGui::IsItemClicked(int mouse_button
)
5183 return IsMouseClicked(mouse_button
) && IsItemHovered(ImGuiHoveredFlags_None
);
5186 bool ImGui::IsAnyItemHovered()
5188 ImGuiContext
& g
= *GImGui
;
5189 return g
.HoveredId
!= 0 || g
.HoveredIdPreviousFrame
!= 0;
5192 bool ImGui::IsAnyItemActive()
5194 ImGuiContext
& g
= *GImGui
;
5195 return g
.ActiveId
!= 0;
5198 bool ImGui::IsAnyItemFocused()
5200 ImGuiContext
& g
= *GImGui
;
5201 return g
.NavId
!= 0 && !g
.NavDisableHighlight
;
5204 bool ImGui::IsItemVisible()
5206 ImGuiWindow
* window
= GetCurrentWindowRead();
5207 return window
->ClipRect
.Overlaps(window
->DC
.LastItemRect
);
5210 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
5211 void ImGui::SetItemAllowOverlap()
5213 ImGuiContext
& g
= *GImGui
;
5214 if (g
.HoveredId
== g
.CurrentWindow
->DC
.LastItemId
)
5215 g
.HoveredIdAllowOverlap
= true;
5216 if (g
.ActiveId
== g
.CurrentWindow
->DC
.LastItemId
)
5217 g
.ActiveIdAllowOverlap
= true;
5220 ImVec2
ImGui::GetItemRectMin()
5222 ImGuiWindow
* window
= GetCurrentWindowRead();
5223 return window
->DC
.LastItemRect
.Min
;
5226 ImVec2
ImGui::GetItemRectMax()
5228 ImGuiWindow
* window
= GetCurrentWindowRead();
5229 return window
->DC
.LastItemRect
.Max
;
5232 ImVec2
ImGui::GetItemRectSize()
5234 ImGuiWindow
* window
= GetCurrentWindowRead();
5235 return window
->DC
.LastItemRect
.GetSize();
5238 static ImRect
GetViewportRect()
5240 ImGuiContext
& g
= *GImGui
;
5241 if (g
.IO
.DisplayVisibleMin
.x
!= g
.IO
.DisplayVisibleMax
.x
&& g
.IO
.DisplayVisibleMin
.y
!= g
.IO
.DisplayVisibleMax
.y
)
5242 return ImRect(g
.IO
.DisplayVisibleMin
, g
.IO
.DisplayVisibleMax
);
5243 return ImRect(0.0f
, 0.0f
, g
.IO
.DisplaySize
.x
, g
.IO
.DisplaySize
.y
);
5246 // Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first.
5247 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags
, bool override_previous_tooltip
)
5249 ImGuiContext
& g
= *GImGui
;
5250 char window_name
[16];
5251 ImFormatString(window_name
, IM_ARRAYSIZE(window_name
), "##Tooltip_%02d", g
.TooltipOverrideCount
);
5252 if (override_previous_tooltip
)
5253 if (ImGuiWindow
* window
= FindWindowByName(window_name
))
5256 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
5257 window
->Hidden
= true;
5258 window
->HiddenFramesRegular
= 1;
5259 ImFormatString(window_name
, IM_ARRAYSIZE(window_name
), "##Tooltip_%02d", ++g
.TooltipOverrideCount
);
5261 ImGuiWindowFlags flags
= ImGuiWindowFlags_Tooltip
|ImGuiWindowFlags_NoInputs
|ImGuiWindowFlags_NoTitleBar
|ImGuiWindowFlags_NoMove
|ImGuiWindowFlags_NoResize
|ImGuiWindowFlags_NoSavedSettings
|ImGuiWindowFlags_AlwaysAutoResize
|ImGuiWindowFlags_NoNav
;
5262 Begin(window_name
, NULL
, flags
| extra_flags
);
5265 void ImGui::SetTooltipV(const char* fmt
, va_list args
)
5267 BeginTooltipEx(0, true);
5272 void ImGui::SetTooltip(const char* fmt
, ...)
5275 va_start(args
, fmt
);
5276 SetTooltipV(fmt
, args
);
5280 void ImGui::BeginTooltip()
5282 ImGuiContext
& g
= *GImGui
;
5283 if (g
.DragDropWithinSourceOrTarget
)
5285 // 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)
5286 // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
5287 // 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.
5288 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
5289 ImVec2 tooltip_pos
= g
.IO
.MousePos
+ ImVec2(16 * g
.Style
.MouseCursorScale
, 8 * g
.Style
.MouseCursorScale
);
5290 SetNextWindowPos(tooltip_pos
);
5291 SetNextWindowBgAlpha(g
.Style
.Colors
[ImGuiCol_PopupBg
].w
* 0.60f
);
5292 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
5293 BeginTooltipEx(0, true);
5297 BeginTooltipEx(0, false);
5301 void ImGui::EndTooltip()
5303 IM_ASSERT(GetCurrentWindowRead()->Flags
& ImGuiWindowFlags_Tooltip
); // Mismatched BeginTooltip()/EndTooltip() calls
5307 // Mark popup as open (toggle toward open state).
5308 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
5309 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
5310 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
5311 void ImGui::OpenPopupEx(ImGuiID id
)
5313 ImGuiContext
& g
= *GImGui
;
5314 ImGuiWindow
* parent_window
= g
.CurrentWindow
;
5315 int current_stack_size
= g
.CurrentPopupStack
.Size
;
5316 ImGuiPopupRef popup_ref
; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
5317 popup_ref
.PopupId
= id
;
5318 popup_ref
.Window
= NULL
;
5319 popup_ref
.ParentWindow
= parent_window
;
5320 popup_ref
.OpenFrameCount
= g
.FrameCount
;
5321 popup_ref
.OpenParentId
= parent_window
->IDStack
.back();
5322 popup_ref
.OpenMousePos
= g
.IO
.MousePos
;
5323 popup_ref
.OpenPopupPos
= NavCalcPreferredRefPos();
5325 //printf("[%05d] OpenPopupEx(0x%08X)\n", g.FrameCount, id);
5326 if (g
.OpenPopupStack
.Size
< current_stack_size
+ 1)
5328 g
.OpenPopupStack
.push_back(popup_ref
);
5332 // 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
5333 // 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
5334 // 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.
5335 if (g
.OpenPopupStack
[current_stack_size
].PopupId
== id
&& g
.OpenPopupStack
[current_stack_size
].OpenFrameCount
== g
.FrameCount
- 1)
5337 g
.OpenPopupStack
[current_stack_size
].OpenFrameCount
= popup_ref
.OpenFrameCount
;
5341 // Close child popups if any, then flag popup for open/reopen
5342 g
.OpenPopupStack
.resize(current_stack_size
+ 1);
5343 g
.OpenPopupStack
[current_stack_size
] = popup_ref
;
5346 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
5347 // This is equivalent to what ClosePopupToLevel() does.
5348 //if (g.OpenPopupStack[current_stack_size].PopupId == id)
5349 // FocusWindow(parent_window);
5353 void ImGui::OpenPopup(const char* str_id
)
5355 ImGuiContext
& g
= *GImGui
;
5356 OpenPopupEx(g
.CurrentWindow
->GetID(str_id
));
5359 void ImGui::ClosePopupsOverWindow(ImGuiWindow
* ref_window
)
5361 ImGuiContext
& g
= *GImGui
;
5362 if (g
.OpenPopupStack
.empty())
5365 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
5366 // Don't close our own child popup windows.
5370 for (n
= 0; n
< g
.OpenPopupStack
.Size
; n
++)
5372 ImGuiPopupRef
& popup
= g
.OpenPopupStack
[n
];
5375 IM_ASSERT((popup
.Window
->Flags
& ImGuiWindowFlags_Popup
) != 0);
5376 if (popup
.Window
->Flags
& ImGuiWindowFlags_ChildWindow
)
5379 // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow)
5380 bool has_focus
= false;
5381 for (int m
= n
; m
< g
.OpenPopupStack
.Size
&& !has_focus
; m
++)
5382 has_focus
= (g
.OpenPopupStack
[m
].Window
&& g
.OpenPopupStack
[m
].Window
->RootWindow
== ref_window
->RootWindow
);
5387 if (n
< g
.OpenPopupStack
.Size
) // This test is not required but it allows to set a convenient breakpoint on the block below
5388 ClosePopupToLevel(n
);
5391 ImGuiWindow
* ImGui::GetFrontMostPopupModal()
5393 ImGuiContext
& g
= *GImGui
;
5394 for (int n
= g
.OpenPopupStack
.Size
-1; n
>= 0; n
--)
5395 if (ImGuiWindow
* popup
= g
.OpenPopupStack
.Data
[n
].Window
)
5396 if (popup
->Flags
& ImGuiWindowFlags_Modal
)
5401 void ImGui::ClosePopupToLevel(int remaining
)
5403 IM_ASSERT(remaining
>= 0);
5404 ImGuiContext
& g
= *GImGui
;
5405 ImGuiWindow
* focus_window
= (remaining
> 0) ? g
.OpenPopupStack
[remaining
-1].Window
: g
.OpenPopupStack
[0].ParentWindow
;
5406 if (g
.NavLayer
== 0)
5407 focus_window
= NavRestoreLastChildNavWindow(focus_window
);
5408 FocusWindow(focus_window
);
5409 focus_window
->DC
.NavHideHighlightOneFrame
= true;
5410 g
.OpenPopupStack
.resize(remaining
);
5413 void ImGui::ClosePopup(ImGuiID id
)
5415 if (!IsPopupOpen(id
))
5417 ImGuiContext
& g
= *GImGui
;
5418 ClosePopupToLevel(g
.OpenPopupStack
.Size
- 1);
5421 // Close the popup we have begin-ed into.
5422 void ImGui::CloseCurrentPopup()
5424 ImGuiContext
& g
= *GImGui
;
5425 int popup_idx
= g
.CurrentPopupStack
.Size
- 1;
5426 if (popup_idx
< 0 || popup_idx
>= g
.OpenPopupStack
.Size
|| g
.CurrentPopupStack
[popup_idx
].PopupId
!= g
.OpenPopupStack
[popup_idx
].PopupId
)
5428 while (popup_idx
> 0 && g
.OpenPopupStack
[popup_idx
].Window
&& (g
.OpenPopupStack
[popup_idx
].Window
->Flags
& ImGuiWindowFlags_ChildMenu
))
5430 ClosePopupToLevel(popup_idx
);
5433 bool ImGui::BeginPopupEx(ImGuiID id
, ImGuiWindowFlags extra_flags
)
5435 ImGuiContext
& g
= *GImGui
;
5436 if (!IsPopupOpen(id
))
5438 g
.NextWindowData
.Clear(); // We behave like Begin() and need to consume those values
5443 if (extra_flags
& ImGuiWindowFlags_ChildMenu
)
5444 ImFormatString(name
, IM_ARRAYSIZE(name
), "##Menu_%02d", g
.CurrentPopupStack
.Size
); // Recycle windows based on depth
5446 ImFormatString(name
, IM_ARRAYSIZE(name
), "##Popup_%08x", id
); // Not recycling, so we can close/open during the same frame
5448 bool is_open
= Begin(name
, NULL
, extra_flags
| ImGuiWindowFlags_Popup
);
5449 if (!is_open
) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
5455 bool ImGui::BeginPopup(const char* str_id
, ImGuiWindowFlags flags
)
5457 ImGuiContext
& g
= *GImGui
;
5458 if (g
.OpenPopupStack
.Size
<= g
.CurrentPopupStack
.Size
) // Early out for performance
5460 g
.NextWindowData
.Clear(); // We behave like Begin() and need to consume those values
5463 return BeginPopupEx(g
.CurrentWindow
->GetID(str_id
), flags
|ImGuiWindowFlags_AlwaysAutoResize
|ImGuiWindowFlags_NoTitleBar
|ImGuiWindowFlags_NoSavedSettings
);
5466 bool ImGui::IsPopupOpen(ImGuiID id
)
5468 ImGuiContext
& g
= *GImGui
;
5469 return g
.OpenPopupStack
.Size
> g
.CurrentPopupStack
.Size
&& g
.OpenPopupStack
[g
.CurrentPopupStack
.Size
].PopupId
== id
;
5472 bool ImGui::IsPopupOpen(const char* str_id
)
5474 ImGuiContext
& g
= *GImGui
;
5475 return g
.OpenPopupStack
.Size
> g
.CurrentPopupStack
.Size
&& g
.OpenPopupStack
[g
.CurrentPopupStack
.Size
].PopupId
== g
.CurrentWindow
->GetID(str_id
);
5478 bool ImGui::BeginPopupModal(const char* name
, bool* p_open
, ImGuiWindowFlags flags
)
5480 ImGuiContext
& g
= *GImGui
;
5481 ImGuiWindow
* window
= g
.CurrentWindow
;
5482 const ImGuiID id
= window
->GetID(name
);
5483 if (!IsPopupOpen(id
))
5485 g
.NextWindowData
.Clear(); // We behave like Begin() and need to consume those values
5489 // Center modal windows by default
5490 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
5491 if (g
.NextWindowData
.PosCond
== 0)
5492 SetNextWindowPos(g
.IO
.DisplaySize
* 0.5f
, ImGuiCond_Appearing
, ImVec2(0.5f
, 0.5f
));
5494 bool is_open
= Begin(name
, p_open
, flags
| ImGuiWindowFlags_Popup
| ImGuiWindowFlags_Modal
| ImGuiWindowFlags_NoCollapse
| ImGuiWindowFlags_NoSavedSettings
);
5495 if (!is_open
|| (p_open
&& !*p_open
)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
5506 void ImGui::NavMoveRequestForward(ImGuiDir move_dir
, ImGuiDir clip_dir
, const ImRect
& bb_rel
, ImGuiNavMoveFlags move_flags
)
5508 ImGuiContext
& g
= *GImGui
;
5509 IM_ASSERT(g
.NavMoveRequestForward
== ImGuiNavForward_None
);
5510 ImGui::NavMoveRequestCancel();
5511 g
.NavMoveDir
= move_dir
;
5512 g
.NavMoveClipDir
= clip_dir
;
5513 g
.NavMoveRequestForward
= ImGuiNavForward_ForwardQueued
;
5514 g
.NavMoveRequestFlags
= move_flags
;
5515 g
.NavWindow
->NavRectRel
[g
.NavLayer
] = bb_rel
;
5518 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow
* window
, ImGuiNavMoveFlags move_flags
)
5520 ImGuiContext
& g
= *GImGui
;
5521 if (g
.NavWindow
!= window
|| !NavMoveRequestButNoResultYet() || g
.NavMoveRequestForward
!= ImGuiNavForward_None
|| g
.NavLayer
!= 0)
5523 IM_ASSERT(move_flags
!= 0); // No points calling this with no wrapping
5524 ImRect bb_rel
= window
->NavRectRel
[0];
5526 ImGuiDir clip_dir
= g
.NavMoveDir
;
5527 if (g
.NavMoveDir
== ImGuiDir_Left
&& (move_flags
& (ImGuiNavMoveFlags_WrapX
| ImGuiNavMoveFlags_LoopX
)))
5529 bb_rel
.Min
.x
= bb_rel
.Max
.x
= ImMax(window
->SizeFull
.x
, window
->SizeContents
.x
) - window
->Scroll
.x
;
5530 if (move_flags
& ImGuiNavMoveFlags_WrapX
) { bb_rel
.TranslateY(-bb_rel
.GetHeight()); clip_dir
= ImGuiDir_Up
; }
5531 NavMoveRequestForward(g
.NavMoveDir
, clip_dir
, bb_rel
, move_flags
);
5533 if (g
.NavMoveDir
== ImGuiDir_Right
&& (move_flags
& (ImGuiNavMoveFlags_WrapX
| ImGuiNavMoveFlags_LoopX
)))
5535 bb_rel
.Min
.x
= bb_rel
.Max
.x
= -window
->Scroll
.x
;
5536 if (move_flags
& ImGuiNavMoveFlags_WrapX
) { bb_rel
.TranslateY(+bb_rel
.GetHeight()); clip_dir
= ImGuiDir_Down
; }
5537 NavMoveRequestForward(g
.NavMoveDir
, clip_dir
, bb_rel
, move_flags
);
5539 if (g
.NavMoveDir
== ImGuiDir_Up
&& (move_flags
& (ImGuiNavMoveFlags_WrapY
| ImGuiNavMoveFlags_LoopY
)))
5541 bb_rel
.Min
.y
= bb_rel
.Max
.y
= ImMax(window
->SizeFull
.y
, window
->SizeContents
.y
) - window
->Scroll
.y
;
5542 if (move_flags
& ImGuiNavMoveFlags_WrapY
) { bb_rel
.TranslateX(-bb_rel
.GetWidth()); clip_dir
= ImGuiDir_Left
; }
5543 NavMoveRequestForward(g
.NavMoveDir
, clip_dir
, bb_rel
, move_flags
);
5545 if (g
.NavMoveDir
== ImGuiDir_Down
&& (move_flags
& (ImGuiNavMoveFlags_WrapY
| ImGuiNavMoveFlags_LoopY
)))
5547 bb_rel
.Min
.y
= bb_rel
.Max
.y
= -window
->Scroll
.y
;
5548 if (move_flags
& ImGuiNavMoveFlags_WrapY
) { bb_rel
.TranslateX(+bb_rel
.GetWidth()); clip_dir
= ImGuiDir_Right
; }
5549 NavMoveRequestForward(g
.NavMoveDir
, clip_dir
, bb_rel
, move_flags
);
5553 void ImGui::EndPopup()
5555 ImGuiContext
& g
= *GImGui
; (void)g
;
5556 IM_ASSERT(g
.CurrentWindow
->Flags
& ImGuiWindowFlags_Popup
); // Mismatched BeginPopup()/EndPopup() calls
5557 IM_ASSERT(g
.CurrentPopupStack
.Size
> 0);
5559 // Make all menus and popups wrap around for now, may need to expose that policy.
5560 NavMoveRequestTryWrapping(g
.CurrentWindow
, ImGuiNavMoveFlags_LoopY
);
5565 bool ImGui::OpenPopupOnItemClick(const char* str_id
, int mouse_button
)
5567 ImGuiWindow
* window
= GImGui
->CurrentWindow
;
5568 if (IsMouseReleased(mouse_button
) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup
))
5570 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!
5571 IM_ASSERT(id
!= 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
5578 // This is a helper to handle the simplest case of associating one named popup to one given widget.
5579 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
5580 // You can pass a NULL str_id to use the identifier of the last item.
5581 bool ImGui::BeginPopupContextItem(const char* str_id
, int mouse_button
)
5583 ImGuiWindow
* window
= GImGui
->CurrentWindow
;
5584 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!
5585 IM_ASSERT(id
!= 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
5586 if (IsMouseReleased(mouse_button
) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup
))
5588 return BeginPopupEx(id
, ImGuiWindowFlags_AlwaysAutoResize
|ImGuiWindowFlags_NoTitleBar
|ImGuiWindowFlags_NoSavedSettings
);
5591 bool ImGui::BeginPopupContextWindow(const char* str_id
, int mouse_button
, bool also_over_items
)
5594 str_id
= "window_context";
5595 ImGuiID id
= GImGui
->CurrentWindow
->GetID(str_id
);
5596 if (IsMouseReleased(mouse_button
) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup
))
5597 if (also_over_items
|| !IsAnyItemHovered())
5599 return BeginPopupEx(id
, ImGuiWindowFlags_AlwaysAutoResize
|ImGuiWindowFlags_NoTitleBar
|ImGuiWindowFlags_NoSavedSettings
);
5602 bool ImGui::BeginPopupContextVoid(const char* str_id
, int mouse_button
)
5605 str_id
= "void_context";
5606 ImGuiID id
= GImGui
->CurrentWindow
->GetID(str_id
);
5607 if (IsMouseReleased(mouse_button
) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow
))
5609 return BeginPopupEx(id
, ImGuiWindowFlags_AlwaysAutoResize
|ImGuiWindowFlags_NoTitleBar
|ImGuiWindowFlags_NoSavedSettings
);
5612 static bool ImGui::BeginChildEx(const char* name
, ImGuiID id
, const ImVec2
& size_arg
, bool border
, ImGuiWindowFlags flags
)
5614 ImGuiContext
& g
= *GImGui
;
5615 ImGuiWindow
* parent_window
= g
.CurrentWindow
;
5617 flags
|= ImGuiWindowFlags_NoTitleBar
|ImGuiWindowFlags_NoResize
|ImGuiWindowFlags_NoSavedSettings
|ImGuiWindowFlags_ChildWindow
;
5618 flags
|= (parent_window
->Flags
& ImGuiWindowFlags_NoMove
); // Inherit the NoMove flag
5621 const ImVec2 content_avail
= GetContentRegionAvail();
5622 ImVec2 size
= ImFloor(size_arg
);
5623 const int auto_fit_axises
= ((size
.x
== 0.0f
) ? (1 << ImGuiAxis_X
) : 0x00) | ((size
.y
== 0.0f
) ? (1 << ImGuiAxis_Y
) : 0x00);
5625 size
.x
= ImMax(content_avail
.x
+ size
.x
, 4.0f
); // Arbitrary minimum child size (0.0f causing too much issues)
5627 size
.y
= ImMax(content_avail
.y
+ size
.y
, 4.0f
);
5628 SetNextWindowSize(size
);
5633 ImFormatString(title
, IM_ARRAYSIZE(title
), "%s/%s", parent_window
->Name
, name
);
5635 ImFormatString(title
, IM_ARRAYSIZE(title
), "%s/%08X", parent_window
->Name
, id
);
5637 const float backup_border_size
= g
.Style
.ChildBorderSize
;
5639 g
.Style
.ChildBorderSize
= 0.0f
;
5640 bool ret
= Begin(title
, NULL
, flags
);
5641 g
.Style
.ChildBorderSize
= backup_border_size
;
5643 ImGuiWindow
* child_window
= g
.CurrentWindow
;
5644 child_window
->ChildId
= id
;
5645 child_window
->AutoFitChildAxises
= auto_fit_axises
;
5647 // Process navigation-in immediately so NavInit can run on first frame
5648 if (g
.NavActivateId
== id
&& !(flags
& ImGuiWindowFlags_NavFlattened
) && (child_window
->DC
.NavLayerActiveMask
!= 0 || child_window
->DC
.NavHasScroll
))
5650 FocusWindow(child_window
);
5651 NavInitWindow(child_window
, false);
5652 SetActiveID(id
+1, child_window
); // Steal ActiveId with a dummy id so that key-press won't activate child item
5653 g
.ActiveIdSource
= ImGuiInputSource_Nav
;
5659 bool ImGui::BeginChild(const char* str_id
, const ImVec2
& size_arg
, bool border
, ImGuiWindowFlags extra_flags
)
5661 ImGuiWindow
* window
= GetCurrentWindow();
5662 return BeginChildEx(str_id
, window
->GetID(str_id
), size_arg
, border
, extra_flags
);
5665 bool ImGui::BeginChild(ImGuiID id
, const ImVec2
& size_arg
, bool border
, ImGuiWindowFlags extra_flags
)
5668 return BeginChildEx(NULL
, id
, size_arg
, border
, extra_flags
);
5671 void ImGui::EndChild()
5673 ImGuiContext
& g
= *GImGui
;
5674 ImGuiWindow
* window
= g
.CurrentWindow
;
5676 IM_ASSERT(window
->Flags
& ImGuiWindowFlags_ChildWindow
); // Mismatched BeginChild()/EndChild() callss
5677 if (window
->BeginCount
> 1)
5683 ImVec2 sz
= window
->Size
;
5684 if (window
->AutoFitChildAxises
& (1 << ImGuiAxis_X
)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
5685 sz
.x
= ImMax(4.0f
, sz
.x
);
5686 if (window
->AutoFitChildAxises
& (1 << ImGuiAxis_Y
))
5687 sz
.y
= ImMax(4.0f
, sz
.y
);
5690 ImGuiWindow
* parent_window
= g
.CurrentWindow
;
5691 ImRect
bb(parent_window
->DC
.CursorPos
, parent_window
->DC
.CursorPos
+ sz
);
5693 if ((window
->DC
.NavLayerActiveMask
!= 0 || window
->DC
.NavHasScroll
) && !(window
->Flags
& ImGuiWindowFlags_NavFlattened
))
5695 ItemAdd(bb
, window
->ChildId
);
5696 RenderNavHighlight(bb
, window
->ChildId
);
5698 // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
5699 if (window
->DC
.NavLayerActiveMask
== 0 && window
== g
.NavWindow
)
5700 RenderNavHighlight(ImRect(bb
.Min
- ImVec2(2,2), bb
.Max
+ ImVec2(2,2)), g
.NavId
, ImGuiNavHighlightFlags_TypeThin
);
5704 // Not navigable into
5710 // Helper to create a child window / scrolling region that looks like a normal widget frame.
5711 bool ImGui::BeginChildFrame(ImGuiID id
, const ImVec2
& size
, ImGuiWindowFlags extra_flags
)
5713 ImGuiContext
& g
= *GImGui
;
5714 const ImGuiStyle
& style
= g
.Style
;
5715 PushStyleColor(ImGuiCol_ChildBg
, style
.Colors
[ImGuiCol_FrameBg
]);
5716 PushStyleVar(ImGuiStyleVar_ChildRounding
, style
.FrameRounding
);
5717 PushStyleVar(ImGuiStyleVar_ChildBorderSize
, style
.FrameBorderSize
);
5718 PushStyleVar(ImGuiStyleVar_WindowPadding
, style
.FramePadding
);
5719 bool ret
= BeginChild(id
, size
, true, ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_AlwaysUseWindowPadding
| extra_flags
);
5725 void ImGui::EndChildFrame()
5730 // Save and compare stack sizes on Begin()/End() to detect usage errors
5731 static void CheckStacksSize(ImGuiWindow
* window
, bool write
)
5733 // 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)
5734 ImGuiContext
& g
= *GImGui
;
5735 int* p_backup
= &window
->DC
.StackSizesBackup
[0];
5736 { 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()
5737 { 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()
5738 { 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()
5739 // 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.
5740 { 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()
5741 { 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()
5742 { 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()
5743 IM_ASSERT(p_backup
== window
->DC
.StackSizesBackup
+ IM_ARRAYSIZE(window
->DC
.StackSizesBackup
));
5746 enum ImGuiPopupPositionPolicy
5748 ImGuiPopupPositionPolicy_Default
,
5749 ImGuiPopupPositionPolicy_ComboBox
5752 static ImRect
FindAllowedExtentRectForWindow(ImGuiWindow
*)
5754 ImVec2 padding
= GImGui
->Style
.DisplaySafeAreaPadding
;
5755 ImRect r_screen
= GetViewportRect();
5756 r_screen
.Expand(ImVec2((r_screen
.GetWidth() > padding
.x
* 2) ? -padding
.x
: 0.0f
, (r_screen
.GetHeight() > padding
.y
* 2) ? -padding
.y
: 0.0f
));
5760 // 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.)
5761 // 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.
5762 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
)
5764 ImVec2 base_pos_clamped
= ImClamp(ref_pos
, r_outer
.Min
, r_outer
.Max
- size
);
5765 //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
5766 //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
5768 // Combo Box policy (we want a connecting edge)
5769 if (policy
== ImGuiPopupPositionPolicy_ComboBox
)
5771 const ImGuiDir dir_prefered_order
[ImGuiDir_COUNT
] = { ImGuiDir_Down
, ImGuiDir_Right
, ImGuiDir_Left
, ImGuiDir_Up
};
5772 for (int n
= (*last_dir
!= ImGuiDir_None
) ? -1 : 0; n
< ImGuiDir_COUNT
; n
++)
5774 const ImGuiDir dir
= (n
== -1) ? *last_dir
: dir_prefered_order
[n
];
5775 if (n
!= -1 && dir
== *last_dir
) // Already tried this direction?
5778 if (dir
== ImGuiDir_Down
) pos
= ImVec2(r_avoid
.Min
.x
, r_avoid
.Max
.y
); // Below, Toward Right (default)
5779 if (dir
== ImGuiDir_Right
) pos
= ImVec2(r_avoid
.Min
.x
, r_avoid
.Min
.y
- size
.y
); // Above, Toward Right
5780 if (dir
== ImGuiDir_Left
) pos
= ImVec2(r_avoid
.Max
.x
- size
.x
, r_avoid
.Max
.y
); // Below, Toward Left
5781 if (dir
== ImGuiDir_Up
) pos
= ImVec2(r_avoid
.Max
.x
- size
.x
, r_avoid
.Min
.y
- size
.y
); // Above, Toward Left
5782 if (!r_outer
.Contains(ImRect(pos
, pos
+ size
)))
5789 // Default popup policy
5790 const ImGuiDir dir_prefered_order
[ImGuiDir_COUNT
] = { ImGuiDir_Right
, ImGuiDir_Down
, ImGuiDir_Up
, ImGuiDir_Left
};
5791 for (int n
= (*last_dir
!= ImGuiDir_None
) ? -1 : 0; n
< ImGuiDir_COUNT
; n
++)
5793 const ImGuiDir dir
= (n
== -1) ? *last_dir
: dir_prefered_order
[n
];
5794 if (n
!= -1 && dir
== *last_dir
) // Already tried this direction?
5796 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
);
5797 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
);
5798 if (avail_w
< size
.x
|| avail_h
< size
.y
)
5801 pos
.x
= (dir
== ImGuiDir_Left
) ? r_avoid
.Min
.x
- size
.x
: (dir
== ImGuiDir_Right
) ? r_avoid
.Max
.x
: base_pos_clamped
.x
;
5802 pos
.y
= (dir
== ImGuiDir_Up
) ? r_avoid
.Min
.y
- size
.y
: (dir
== ImGuiDir_Down
) ? r_avoid
.Max
.y
: base_pos_clamped
.y
;
5807 // Fallback, try to keep within display
5808 *last_dir
= ImGuiDir_None
;
5809 ImVec2 pos
= ref_pos
;
5810 pos
.x
= ImMax(ImMin(pos
.x
+ size
.x
, r_outer
.Max
.x
) - size
.x
, r_outer
.Min
.x
);
5811 pos
.y
= ImMax(ImMin(pos
.y
+ size
.y
, r_outer
.Max
.y
) - size
.y
, r_outer
.Min
.y
);
5815 static ImVec2
FindBestWindowPosForPopup(ImGuiWindow
* window
)
5817 ImGuiContext
& g
= *GImGui
;
5819 ImRect r_outer
= FindAllowedExtentRectForWindow(window
);
5820 if (window
->Flags
& ImGuiWindowFlags_ChildMenu
)
5822 // 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.
5823 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
5824 IM_ASSERT(g
.CurrentWindow
== window
);
5825 ImGuiWindow
* parent_window
= g
.CurrentWindowStack
[g
.CurrentWindowStack
.Size
- 2];
5826 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).
5828 if (parent_window
->DC
.MenuBarAppending
)
5829 r_avoid
= ImRect(-FLT_MAX
, parent_window
->Pos
.y
+ parent_window
->TitleBarHeight(), FLT_MAX
, parent_window
->Pos
.y
+ parent_window
->TitleBarHeight() + parent_window
->MenuBarHeight());
5831 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
);
5832 return FindBestWindowPosForPopupEx(window
->Pos
, window
->Size
, &window
->AutoPosLastDirection
, r_outer
, r_avoid
);
5834 if (window
->Flags
& ImGuiWindowFlags_Popup
)
5836 ImRect r_avoid
= ImRect(window
->Pos
.x
- 1, window
->Pos
.y
- 1, window
->Pos
.x
+ 1, window
->Pos
.y
+ 1);
5837 return FindBestWindowPosForPopupEx(window
->Pos
, window
->Size
, &window
->AutoPosLastDirection
, r_outer
, r_avoid
);
5839 if (window
->Flags
& ImGuiWindowFlags_Tooltip
)
5841 // Position tooltip (always follows mouse)
5842 float sc
= g
.Style
.MouseCursorScale
;
5843 ImVec2 ref_pos
= NavCalcPreferredRefPos();
5845 if (!g
.NavDisableHighlight
&& g
.NavDisableMouseHover
&& !(g
.IO
.ConfigFlags
& ImGuiConfigFlags_NavEnableSetMousePos
))
5846 r_avoid
= ImRect(ref_pos
.x
- 16, ref_pos
.y
- 8, ref_pos
.x
+ 16, ref_pos
.y
+ 8);
5848 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.
5849 ImVec2 pos
= FindBestWindowPosForPopupEx(ref_pos
, window
->Size
, &window
->AutoPosLastDirection
, r_outer
, r_avoid
);
5850 if (window
->AutoPosLastDirection
== ImGuiDir_None
)
5851 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.
5858 static void SetWindowConditionAllowFlags(ImGuiWindow
* window
, ImGuiCond flags
, bool enabled
)
5860 window
->SetWindowPosAllowFlags
= enabled
? (window
->SetWindowPosAllowFlags
| flags
) : (window
->SetWindowPosAllowFlags
& ~flags
);
5861 window
->SetWindowSizeAllowFlags
= enabled
? (window
->SetWindowSizeAllowFlags
| flags
) : (window
->SetWindowSizeAllowFlags
& ~flags
);
5862 window
->SetWindowCollapsedAllowFlags
= enabled
? (window
->SetWindowCollapsedAllowFlags
| flags
) : (window
->SetWindowCollapsedAllowFlags
& ~flags
);
5865 ImGuiWindow
* ImGui::FindWindowByName(const char* name
)
5867 ImGuiContext
& g
= *GImGui
;
5868 ImGuiID id
= ImHash(name
, 0);
5869 return (ImGuiWindow
*)g
.WindowsById
.GetVoidPtr(id
);
5872 static ImGuiWindow
* CreateNewWindow(const char* name
, ImVec2 size
, ImGuiWindowFlags flags
)
5874 ImGuiContext
& g
= *GImGui
;
5876 // Create window the first time
5877 ImGuiWindow
* window
= IM_NEW(ImGuiWindow
)(&g
, name
);
5878 window
->Flags
= flags
;
5879 g
.WindowsById
.SetVoidPtr(window
->ID
, window
);
5881 // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
5882 window
->Pos
= ImVec2(60, 60);
5884 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
5885 if (!(flags
& ImGuiWindowFlags_NoSavedSettings
))
5886 if (ImGuiWindowSettings
* settings
= ImGui::FindWindowSettings(window
->ID
))
5888 // Retrieve settings from .ini file
5889 window
->SettingsIdx
= g
.SettingsWindows
.index_from_pointer(settings
);
5890 SetWindowConditionAllowFlags(window
, ImGuiCond_FirstUseEver
, false);
5891 window
->Pos
= ImFloor(settings
->Pos
);
5892 window
->Collapsed
= settings
->Collapsed
;
5893 if (ImLengthSqr(settings
->Size
) > 0.00001f
)
5894 size
= ImFloor(settings
->Size
);
5896 window
->Size
= window
->SizeFull
= window
->SizeFullAtLastBegin
= size
;
5897 window
->DC
.CursorMaxPos
= window
->Pos
; // So first call to CalcSizeContents() doesn't return crazy values
5899 if ((flags
& ImGuiWindowFlags_AlwaysAutoResize
) != 0)
5901 window
->AutoFitFramesX
= window
->AutoFitFramesY
= 2;
5902 window
->AutoFitOnlyGrows
= false;
5906 if (window
->Size
.x
<= 0.0f
)
5907 window
->AutoFitFramesX
= 2;
5908 if (window
->Size
.y
<= 0.0f
)
5909 window
->AutoFitFramesY
= 2;
5910 window
->AutoFitOnlyGrows
= (window
->AutoFitFramesX
> 0) || (window
->AutoFitFramesY
> 0);
5913 if (flags
& ImGuiWindowFlags_NoBringToFrontOnFocus
)
5914 g
.Windows
.insert(g
.Windows
.begin(), window
); // Quite slow but rare and only once
5916 g
.Windows
.push_back(window
);
5920 static ImVec2
CalcSizeAfterConstraint(ImGuiWindow
* window
, ImVec2 new_size
)
5922 ImGuiContext
& g
= *GImGui
;
5923 if (g
.NextWindowData
.SizeConstraintCond
!= 0)
5925 // Using -1,-1 on either X/Y axis to preserve the current size.
5926 ImRect cr
= g
.NextWindowData
.SizeConstraintRect
;
5927 new_size
.x
= (cr
.Min
.x
>= 0 && cr
.Max
.x
>= 0) ? ImClamp(new_size
.x
, cr
.Min
.x
, cr
.Max
.x
) : window
->SizeFull
.x
;
5928 new_size
.y
= (cr
.Min
.y
>= 0 && cr
.Max
.y
>= 0) ? ImClamp(new_size
.y
, cr
.Min
.y
, cr
.Max
.y
) : window
->SizeFull
.y
;
5929 if (g
.NextWindowData
.SizeCallback
)
5931 ImGuiSizeCallbackData data
;
5932 data
.UserData
= g
.NextWindowData
.SizeCallbackUserData
;
5933 data
.Pos
= window
->Pos
;
5934 data
.CurrentSize
= window
->SizeFull
;
5935 data
.DesiredSize
= new_size
;
5936 g
.NextWindowData
.SizeCallback(&data
);
5937 new_size
= data
.DesiredSize
;
5942 if (!(window
->Flags
& (ImGuiWindowFlags_ChildWindow
| ImGuiWindowFlags_AlwaysAutoResize
)))
5944 new_size
= ImMax(new_size
, g
.Style
.WindowMinSize
);
5945 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
5950 static ImVec2
CalcSizeContents(ImGuiWindow
* window
)
5953 sz
.x
= (float)(int)((window
->SizeContentsExplicit
.x
!= 0.0f
) ? window
->SizeContentsExplicit
.x
: (window
->DC
.CursorMaxPos
.x
- window
->Pos
.x
+ window
->Scroll
.x
));
5954 sz
.y
= (float)(int)((window
->SizeContentsExplicit
.y
!= 0.0f
) ? window
->SizeContentsExplicit
.y
: (window
->DC
.CursorMaxPos
.y
- window
->Pos
.y
+ window
->Scroll
.y
));
5955 return sz
+ window
->WindowPadding
;
5958 static ImVec2
CalcSizeAutoFit(ImGuiWindow
* window
, const ImVec2
& size_contents
)
5960 ImGuiContext
& g
= *GImGui
;
5961 ImGuiStyle
& style
= g
.Style
;
5962 if (window
->Flags
& ImGuiWindowFlags_Tooltip
)
5964 // Tooltip always resize
5965 return size_contents
;
5969 // 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.
5970 const bool is_popup
= (window
->Flags
& ImGuiWindowFlags_Popup
) != 0;
5971 const bool is_menu
= (window
->Flags
& ImGuiWindowFlags_ChildMenu
) != 0;
5972 ImVec2 size_min
= style
.WindowMinSize
;
5973 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)
5974 size_min
= ImMin(size_min
, ImVec2(4.0f
, 4.0f
));
5975 ImVec2 size_auto_fit
= ImClamp(size_contents
, size_min
, ImMax(size_min
, g
.IO
.DisplaySize
- style
.DisplaySafeAreaPadding
* 2.0f
));
5976 ImVec2 size_auto_fit_after_constraint
= CalcSizeAfterConstraint(window
, size_auto_fit
);
5977 if (size_auto_fit_after_constraint
.x
< size_contents
.x
&& !(window
->Flags
& ImGuiWindowFlags_NoScrollbar
) && (window
->Flags
& ImGuiWindowFlags_HorizontalScrollbar
))
5978 size_auto_fit
.y
+= style
.ScrollbarSize
;
5979 if (size_auto_fit_after_constraint
.y
< size_contents
.y
&& !(window
->Flags
& ImGuiWindowFlags_NoScrollbar
))
5980 size_auto_fit
.x
+= style
.ScrollbarSize
;
5981 return size_auto_fit
;
5985 static float GetScrollMaxX(ImGuiWindow
* window
)
5987 return ImMax(0.0f
, window
->SizeContents
.x
- (window
->SizeFull
.x
- window
->ScrollbarSizes
.x
));
5990 static float GetScrollMaxY(ImGuiWindow
* window
)
5992 return ImMax(0.0f
, window
->SizeContents
.y
- (window
->SizeFull
.y
- window
->ScrollbarSizes
.y
));
5995 static ImVec2
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow
* window
, bool snap_on_edges
)
5997 ImGuiContext
& g
= *GImGui
;
5998 ImVec2 scroll
= window
->Scroll
;
5999 if (window
->ScrollTarget
.x
< FLT_MAX
)
6001 float cr_x
= window
->ScrollTargetCenterRatio
.x
;
6002 scroll
.x
= window
->ScrollTarget
.x
- cr_x
* (window
->SizeFull
.x
- window
->ScrollbarSizes
.x
);
6004 if (window
->ScrollTarget
.y
< FLT_MAX
)
6006 // 'snap_on_edges' allows for a discontinuity at the edge of scrolling limits to take account of WindowPadding so that scrolling to make the last item visible scroll far enough to see the padding.
6007 float cr_y
= window
->ScrollTargetCenterRatio
.y
;
6008 float target_y
= window
->ScrollTarget
.y
;
6009 if (snap_on_edges
&& cr_y
<= 0.0f
&& target_y
<= window
->WindowPadding
.y
)
6011 if (snap_on_edges
&& cr_y
>= 1.0f
&& target_y
>= window
->SizeContents
.y
- window
->WindowPadding
.y
+ g
.Style
.ItemSpacing
.y
)
6012 target_y
= window
->SizeContents
.y
;
6013 scroll
.y
= target_y
- (1.0f
- cr_y
) * (window
->TitleBarHeight() + window
->MenuBarHeight()) - cr_y
* (window
->SizeFull
.y
- window
->ScrollbarSizes
.y
);
6015 scroll
= ImMax(scroll
, ImVec2(0.0f
, 0.0f
));
6016 if (!window
->Collapsed
&& !window
->SkipItems
)
6018 scroll
.x
= ImMin(scroll
.x
, GetScrollMaxX(window
));
6019 scroll
.y
= ImMin(scroll
.y
, GetScrollMaxY(window
));
6024 static ImGuiCol
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags
)
6026 if (flags
& (ImGuiWindowFlags_Tooltip
| ImGuiWindowFlags_Popup
))
6027 return ImGuiCol_PopupBg
;
6028 if (flags
& ImGuiWindowFlags_ChildWindow
)
6029 return ImGuiCol_ChildBg
;
6030 return ImGuiCol_WindowBg
;
6033 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow
* window
, const ImVec2
& corner_target
, const ImVec2
& corner_norm
, ImVec2
* out_pos
, ImVec2
* out_size
)
6035 ImVec2 pos_min
= ImLerp(corner_target
, window
->Pos
, corner_norm
); // Expected window upper-left
6036 ImVec2 pos_max
= ImLerp(window
->Pos
+ window
->Size
, corner_target
, corner_norm
); // Expected window lower-right
6037 ImVec2 size_expected
= pos_max
- pos_min
;
6038 ImVec2 size_constrained
= CalcSizeAfterConstraint(window
, size_expected
);
6040 if (corner_norm
.x
== 0.0f
)
6041 out_pos
->x
-= (size_constrained
.x
- size_expected
.x
);
6042 if (corner_norm
.y
== 0.0f
)
6043 out_pos
->y
-= (size_constrained
.y
- size_expected
.y
);
6044 *out_size
= size_constrained
;
6047 struct ImGuiResizeGripDef
6051 int AngleMin12
, AngleMax12
;
6054 const ImGuiResizeGripDef resize_grip_def
[4] =
6056 { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right
6057 { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left
6058 { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left
6059 { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right
6062 static ImRect
GetBorderRect(ImGuiWindow
* window
, int border_n
, float perp_padding
, float thickness
)
6064 ImRect rect
= window
->Rect();
6065 if (thickness
== 0.0f
) rect
.Max
-= ImVec2(1,1);
6066 if (border_n
== 0) return ImRect(rect
.Min
.x
+ perp_padding
, rect
.Min
.y
, rect
.Max
.x
- perp_padding
, rect
.Min
.y
+ thickness
);
6067 if (border_n
== 1) return ImRect(rect
.Max
.x
- thickness
, rect
.Min
.y
+ perp_padding
, rect
.Max
.x
, rect
.Max
.y
- perp_padding
);
6068 if (border_n
== 2) return ImRect(rect
.Min
.x
+ perp_padding
, rect
.Max
.y
- thickness
, rect
.Max
.x
- perp_padding
, rect
.Max
.y
);
6069 if (border_n
== 3) return ImRect(rect
.Min
.x
, rect
.Min
.y
+ perp_padding
, rect
.Min
.x
+ thickness
, rect
.Max
.y
- perp_padding
);
6074 // Handle resize for: Resize Grips, Borders, Gamepad
6075 static void ImGui::UpdateManualResize(ImGuiWindow
* window
, const ImVec2
& size_auto_fit
, int* border_held
, int resize_grip_count
, ImU32 resize_grip_col
[4])
6077 ImGuiContext
& g
= *GImGui
;
6078 ImGuiWindowFlags flags
= window
->Flags
;
6079 if ((flags
& ImGuiWindowFlags_NoResize
) || (flags
& ImGuiWindowFlags_AlwaysAutoResize
) || window
->AutoFitFramesX
> 0 || window
->AutoFitFramesY
> 0)
6082 const int resize_border_count
= g
.IO
.OptResizeWindowsFromEdges
? 4 : 0;
6083 const float grip_draw_size
= (float)(int)ImMax(g
.FontSize
* 1.35f
, window
->WindowRounding
+ 1.0f
+ g
.FontSize
* 0.2f
);
6084 const float grip_hover_size
= (float)(int)(grip_draw_size
* 0.75f
);
6086 ImVec2
pos_target(FLT_MAX
, FLT_MAX
);
6087 ImVec2
size_target(FLT_MAX
, FLT_MAX
);
6089 // Manual resize grips
6091 for (int resize_grip_n
= 0; resize_grip_n
< resize_grip_count
; resize_grip_n
++)
6093 const ImGuiResizeGripDef
& grip
= resize_grip_def
[resize_grip_n
];
6094 const ImVec2 corner
= ImLerp(window
->Pos
, window
->Pos
+ window
->Size
, grip
.CornerPos
);
6096 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
6097 ImRect
resize_rect(corner
, corner
+ grip
.InnerDir
* grip_hover_size
);
6098 if (resize_rect
.Min
.x
> resize_rect
.Max
.x
) ImSwap(resize_rect
.Min
.x
, resize_rect
.Max
.x
);
6099 if (resize_rect
.Min
.y
> resize_rect
.Max
.y
) ImSwap(resize_rect
.Min
.y
, resize_rect
.Max
.y
);
6101 ButtonBehavior(resize_rect
, window
->GetID((void*)(intptr_t)resize_grip_n
), &hovered
, &held
, ImGuiButtonFlags_FlattenChildren
| ImGuiButtonFlags_NoNavFocus
);
6102 if (hovered
|| held
)
6103 g
.MouseCursor
= (resize_grip_n
& 1) ? ImGuiMouseCursor_ResizeNESW
: ImGuiMouseCursor_ResizeNWSE
;
6105 if (held
&& g
.IO
.MouseDoubleClicked
[0] && resize_grip_n
== 0)
6107 // Manual auto-fit when double-clicking
6108 size_target
= CalcSizeAfterConstraint(window
, size_auto_fit
);
6113 // Resize from any of the four corners
6114 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
6115 ImVec2 corner_target
= g
.IO
.MousePos
- g
.ActiveIdClickOffset
+ resize_rect
.GetSize() * grip
.CornerPos
; // Corner of the window corresponding to our corner grip
6116 CalcResizePosSizeFromAnyCorner(window
, corner_target
, grip
.CornerPos
, &pos_target
, &size_target
);
6118 if (resize_grip_n
== 0 || held
|| hovered
)
6119 resize_grip_col
[resize_grip_n
] = GetColorU32(held
? ImGuiCol_ResizeGripActive
: hovered
? ImGuiCol_ResizeGripHovered
: ImGuiCol_ResizeGrip
);
6121 for (int border_n
= 0; border_n
< resize_border_count
; border_n
++)
6123 const float BORDER_SIZE
= 5.0f
; // FIXME: Only works _inside_ window because of HoveredWindow check.
6124 const float BORDER_APPEAR_TIMER
= 0.05f
; // Reduce visual noise
6126 ImRect border_rect
= GetBorderRect(window
, border_n
, grip_hover_size
, BORDER_SIZE
);
6127 ButtonBehavior(border_rect
, window
->GetID((void*)(intptr_t)(border_n
+ 4)), &hovered
, &held
, ImGuiButtonFlags_FlattenChildren
);
6128 if ((hovered
&& g
.HoveredIdTimer
> BORDER_APPEAR_TIMER
) || held
)
6130 g
.MouseCursor
= (border_n
& 1) ? ImGuiMouseCursor_ResizeEW
: ImGuiMouseCursor_ResizeNS
;
6131 if (held
) *border_held
= border_n
;
6135 ImVec2 border_target
= window
->Pos
;
6137 if (border_n
== 0) { border_posn
= ImVec2(0, 0); border_target
.y
= (g
.IO
.MousePos
.y
- g
.ActiveIdClickOffset
.y
); }
6138 if (border_n
== 1) { border_posn
= ImVec2(1, 0); border_target
.x
= (g
.IO
.MousePos
.x
- g
.ActiveIdClickOffset
.x
+ BORDER_SIZE
); }
6139 if (border_n
== 2) { border_posn
= ImVec2(0, 1); border_target
.y
= (g
.IO
.MousePos
.y
- g
.ActiveIdClickOffset
.y
+ BORDER_SIZE
); }
6140 if (border_n
== 3) { border_posn
= ImVec2(0, 0); border_target
.x
= (g
.IO
.MousePos
.x
- g
.ActiveIdClickOffset
.x
); }
6141 CalcResizePosSizeFromAnyCorner(window
, border_target
, border_posn
, &pos_target
, &size_target
);
6146 // Navigation resize (keyboard/gamepad)
6147 if (g
.NavWindowingTarget
&& g
.NavWindowingTarget
->RootWindow
== window
)
6149 ImVec2 nav_resize_delta
;
6150 if (g
.NavInputSource
== ImGuiInputSource_NavKeyboard
&& g
.IO
.KeyShift
)
6151 nav_resize_delta
= GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard
, ImGuiInputReadMode_Down
);
6152 if (g
.NavInputSource
== ImGuiInputSource_NavGamepad
)
6153 nav_resize_delta
= GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad
, ImGuiInputReadMode_Down
);
6154 if (nav_resize_delta
.x
!= 0.0f
|| nav_resize_delta
.y
!= 0.0f
)
6156 const float NAV_RESIZE_SPEED
= 600.0f
;
6157 nav_resize_delta
*= ImFloor(NAV_RESIZE_SPEED
* g
.IO
.DeltaTime
* ImMin(g
.IO
.DisplayFramebufferScale
.x
, g
.IO
.DisplayFramebufferScale
.y
));
6158 g
.NavWindowingToggleLayer
= false;
6159 g
.NavDisableMouseHover
= true;
6160 resize_grip_col
[0] = GetColorU32(ImGuiCol_ResizeGripActive
);
6161 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
6162 size_target
= CalcSizeAfterConstraint(window
, window
->SizeFull
+ nav_resize_delta
);
6166 // Apply back modified position/size to window
6167 if (size_target
.x
!= FLT_MAX
)
6169 window
->SizeFull
= size_target
;
6170 MarkIniSettingsDirty(window
);
6172 if (pos_target
.x
!= FLT_MAX
)
6174 window
->Pos
= ImFloor(pos_target
);
6175 MarkIniSettingsDirty(window
);
6178 window
->Size
= window
->SizeFull
;
6181 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow
* window
, ImGuiWindowFlags flags
, ImGuiWindow
* parent_window
)
6183 window
->ParentWindow
= parent_window
;
6184 window
->RootWindow
= window
->RootWindowForTitleBarHighlight
= window
->RootWindowForNav
= window
;
6185 if (parent_window
&& (flags
& ImGuiWindowFlags_ChildWindow
) && !(flags
& ImGuiWindowFlags_Tooltip
))
6186 window
->RootWindow
= parent_window
->RootWindow
;
6187 if (parent_window
&& !(flags
& ImGuiWindowFlags_Modal
) && (flags
& (ImGuiWindowFlags_ChildWindow
| ImGuiWindowFlags_Popup
)))
6188 window
->RootWindowForTitleBarHighlight
= parent_window
->RootWindowForTitleBarHighlight
;
6189 while (window
->RootWindowForNav
->Flags
& ImGuiWindowFlags_NavFlattened
)
6190 window
->RootWindowForNav
= window
->RootWindowForNav
->ParentWindow
;
6193 // Push a new ImGui window to add widgets to.
6194 // - 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.
6195 // - Begin/End can be called multiple times during the frame with the same window name to append content.
6196 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
6197 // You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file.
6198 // - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned.
6199 // - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed.
6200 bool ImGui::Begin(const char* name
, bool* p_open
, ImGuiWindowFlags flags
)
6202 ImGuiContext
& g
= *GImGui
;
6203 const ImGuiStyle
& style
= g
.Style
;
6204 IM_ASSERT(name
!= NULL
); // Window name required
6205 IM_ASSERT(g
.Initialized
); // Forgot to call ImGui::NewFrame()
6206 IM_ASSERT(g
.FrameCountEnded
!= g
.FrameCount
); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
6209 ImGuiWindow
* window
= FindWindowByName(name
);
6210 const bool window_just_created
= (window
== NULL
);
6211 if (window_just_created
)
6213 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.
6214 window
= CreateNewWindow(name
, size_on_first_use
, flags
);
6217 // Automatically disable manual moving/resizing when NoInputs is set
6218 if (flags
& ImGuiWindowFlags_NoInputs
)
6219 flags
|= ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoResize
;
6221 if (flags
& ImGuiWindowFlags_NavFlattened
)
6222 IM_ASSERT(flags
& ImGuiWindowFlags_ChildWindow
);
6224 const int current_frame
= g
.FrameCount
;
6225 const bool first_begin_of_the_frame
= (window
->LastFrameActive
!= current_frame
);
6226 if (first_begin_of_the_frame
)
6227 window
->Flags
= (ImGuiWindowFlags
)flags
;
6229 flags
= window
->Flags
;
6231 // 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
6232 ImGuiWindow
* parent_window_in_stack
= g
.CurrentWindowStack
.empty() ? NULL
: g
.CurrentWindowStack
.back();
6233 ImGuiWindow
* parent_window
= first_begin_of_the_frame
? ((flags
& (ImGuiWindowFlags_ChildWindow
| ImGuiWindowFlags_Popup
)) ? parent_window_in_stack
: NULL
) : window
->ParentWindow
;
6234 IM_ASSERT(parent_window
!= NULL
|| !(flags
& ImGuiWindowFlags_ChildWindow
));
6235 window
->HasCloseButton
= (p_open
!= NULL
);
6237 // Update the Appearing flag
6238 bool window_just_activated_by_user
= (window
->LastFrameActive
< current_frame
- 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
6239 const bool window_just_appearing_after_hidden_for_resize
= (window
->HiddenFramesForResize
> 0);
6240 if (flags
& ImGuiWindowFlags_Popup
)
6242 ImGuiPopupRef
& popup_ref
= g
.OpenPopupStack
[g
.CurrentPopupStack
.Size
];
6243 window_just_activated_by_user
|= (window
->PopupId
!= popup_ref
.PopupId
); // We recycle popups so treat window as activated if popup id changed
6244 window_just_activated_by_user
|= (window
!= popup_ref
.Window
);
6246 window
->Appearing
= (window_just_activated_by_user
|| window_just_appearing_after_hidden_for_resize
);
6247 if (window
->Appearing
)
6248 SetWindowConditionAllowFlags(window
, ImGuiCond_Appearing
, true);
6251 g
.CurrentWindowStack
.push_back(window
);
6252 SetCurrentWindow(window
);
6253 CheckStacksSize(window
, true);
6254 if (flags
& ImGuiWindowFlags_Popup
)
6256 ImGuiPopupRef
& popup_ref
= g
.OpenPopupStack
[g
.CurrentPopupStack
.Size
];
6257 popup_ref
.Window
= window
;
6258 g
.CurrentPopupStack
.push_back(popup_ref
);
6259 window
->PopupId
= popup_ref
.PopupId
;
6262 if (window_just_appearing_after_hidden_for_resize
&& !(flags
& ImGuiWindowFlags_ChildWindow
))
6263 window
->NavLastIds
[0] = 0;
6265 // Process SetNextWindow***() calls
6266 bool window_pos_set_by_api
= false;
6267 bool window_size_x_set_by_api
= false, window_size_y_set_by_api
= false;
6268 if (g
.NextWindowData
.PosCond
)
6270 window_pos_set_by_api
= (window
->SetWindowPosAllowFlags
& g
.NextWindowData
.PosCond
) != 0;
6271 if (window_pos_set_by_api
&& ImLengthSqr(g
.NextWindowData
.PosPivotVal
) > 0.00001f
)
6273 // May be processed on the next frame if this is our first frame and we are measuring size
6274 // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
6275 window
->SetWindowPosVal
= g
.NextWindowData
.PosVal
;
6276 window
->SetWindowPosPivot
= g
.NextWindowData
.PosPivotVal
;
6277 window
->SetWindowPosAllowFlags
&= ~(ImGuiCond_Once
| ImGuiCond_FirstUseEver
| ImGuiCond_Appearing
);
6281 SetWindowPos(window
, g
.NextWindowData
.PosVal
, g
.NextWindowData
.PosCond
);
6284 if (g
.NextWindowData
.SizeCond
)
6286 window_size_x_set_by_api
= (window
->SetWindowSizeAllowFlags
& g
.NextWindowData
.SizeCond
) != 0 && (g
.NextWindowData
.SizeVal
.x
> 0.0f
);
6287 window_size_y_set_by_api
= (window
->SetWindowSizeAllowFlags
& g
.NextWindowData
.SizeCond
) != 0 && (g
.NextWindowData
.SizeVal
.y
> 0.0f
);
6288 SetWindowSize(window
, g
.NextWindowData
.SizeVal
, g
.NextWindowData
.SizeCond
);
6290 if (g
.NextWindowData
.ContentSizeCond
)
6292 // Adjust passed "client size" to become a "window size"
6293 window
->SizeContentsExplicit
= g
.NextWindowData
.ContentSizeVal
;
6294 if (window
->SizeContentsExplicit
.y
!= 0.0f
)
6295 window
->SizeContentsExplicit
.y
+= window
->TitleBarHeight() + window
->MenuBarHeight();
6297 else if (first_begin_of_the_frame
)
6299 window
->SizeContentsExplicit
= ImVec2(0.0f
, 0.0f
);
6301 if (g
.NextWindowData
.CollapsedCond
)
6302 SetWindowCollapsed(window
, g
.NextWindowData
.CollapsedVal
, g
.NextWindowData
.CollapsedCond
);
6303 if (g
.NextWindowData
.FocusCond
)
6304 FocusWindow(window
);
6305 if (window
->Appearing
)
6306 SetWindowConditionAllowFlags(window
, ImGuiCond_Appearing
, false);
6308 // When reusing window again multiple times a frame, just append content (don't need to setup again)
6309 if (first_begin_of_the_frame
)
6312 const bool window_is_child_tooltip
= (flags
& ImGuiWindowFlags_ChildWindow
) && (flags
& ImGuiWindowFlags_Tooltip
); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
6313 UpdateWindowParentAndRootLinks(window
, flags
, parent_window
);
6315 window
->Active
= true;
6316 window
->BeginOrderWithinParent
= 0;
6317 window
->BeginOrderWithinContext
= g
.WindowsActiveCount
++;
6318 window
->BeginCount
= 0;
6319 window
->ClipRect
= ImVec4(-FLT_MAX
,-FLT_MAX
,+FLT_MAX
,+FLT_MAX
);
6320 window
->LastFrameActive
= current_frame
;
6321 window
->IDStack
.resize(1);
6323 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
6325 // Update contents size from last frame for auto-fitting (or use explicit size)
6326 window
->SizeContents
= CalcSizeContents(window
);
6327 if (window
->HiddenFramesRegular
> 0)
6328 window
->HiddenFramesRegular
--;
6329 if (window
->HiddenFramesForResize
> 0)
6330 window
->HiddenFramesForResize
--;
6332 // Hide new windows for one frame until they calculate their size
6333 if (window_just_created
&& (!window_size_x_set_by_api
|| !window_size_y_set_by_api
))
6334 window
->HiddenFramesForResize
= 1;
6336 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
6337 // We reset Size/SizeContents for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
6338 if (window_just_activated_by_user
&& (flags
& (ImGuiWindowFlags_Popup
| ImGuiWindowFlags_Tooltip
)) != 0)
6340 window
->HiddenFramesForResize
= 1;
6341 if (flags
& ImGuiWindowFlags_AlwaysAutoResize
)
6343 if (!window_size_x_set_by_api
)
6344 window
->Size
.x
= window
->SizeFull
.x
= 0.f
;
6345 if (!window_size_y_set_by_api
)
6346 window
->Size
.y
= window
->SizeFull
.y
= 0.f
;
6347 window
->SizeContents
= ImVec2(0.f
, 0.f
);
6351 SetCurrentWindow(window
);
6353 // Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies)
6354 window
->WindowBorderSize
= (flags
& ImGuiWindowFlags_ChildWindow
) ? style
.ChildBorderSize
: ((flags
& (ImGuiWindowFlags_Popup
| ImGuiWindowFlags_Tooltip
)) && !(flags
& ImGuiWindowFlags_Modal
)) ? style
.PopupBorderSize
: style
.WindowBorderSize
;
6355 window
->WindowPadding
= style
.WindowPadding
;
6356 if ((flags
& ImGuiWindowFlags_ChildWindow
) && !(flags
& (ImGuiWindowFlags_AlwaysUseWindowPadding
| ImGuiWindowFlags_Popup
)) && window
->WindowBorderSize
== 0.0f
)
6357 window
->WindowPadding
= ImVec2(0.0f
, (flags
& ImGuiWindowFlags_MenuBar
) ? style
.WindowPadding
.y
: 0.0f
);
6358 window
->DC
.MenuBarOffset
.x
= ImMax(ImMax(window
->WindowPadding
.x
, style
.ItemSpacing
.x
), g
.NextWindowData
.MenuBarOffsetMinVal
.x
);
6359 window
->DC
.MenuBarOffset
.y
= g
.NextWindowData
.MenuBarOffsetMinVal
.y
;
6361 // Collapse window by double-clicking on title bar
6362 // 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
6363 if (!(flags
& ImGuiWindowFlags_NoTitleBar
) && !(flags
& ImGuiWindowFlags_NoCollapse
))
6365 // 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.
6366 ImRect title_bar_rect
= window
->TitleBarRect();
6367 if (g
.HoveredWindow
== window
&& g
.HoveredId
== 0 && g
.HoveredIdPreviousFrame
== 0 && IsMouseHoveringRect(title_bar_rect
.Min
, title_bar_rect
.Max
) && g
.IO
.MouseDoubleClicked
[0])
6368 window
->CollapseToggleWanted
= true;
6369 if (window
->CollapseToggleWanted
)
6371 window
->Collapsed
= !window
->Collapsed
;
6372 MarkIniSettingsDirty(window
);
6373 FocusWindow(window
);
6378 window
->Collapsed
= false;
6380 window
->CollapseToggleWanted
= false;
6384 // Calculate auto-fit size, handle automatic resize
6385 const ImVec2 size_auto_fit
= CalcSizeAutoFit(window
, window
->SizeContents
);
6386 ImVec2
size_full_modified(FLT_MAX
, FLT_MAX
);
6387 if ((flags
& ImGuiWindowFlags_AlwaysAutoResize
) && !window
->Collapsed
)
6389 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
6390 if (!window_size_x_set_by_api
)
6391 window
->SizeFull
.x
= size_full_modified
.x
= size_auto_fit
.x
;
6392 if (!window_size_y_set_by_api
)
6393 window
->SizeFull
.y
= size_full_modified
.y
= size_auto_fit
.y
;
6395 else if (window
->AutoFitFramesX
> 0 || window
->AutoFitFramesY
> 0)
6397 // Auto-fit may only grow window during the first few frames
6398 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
6399 if (!window_size_x_set_by_api
&& window
->AutoFitFramesX
> 0)
6400 window
->SizeFull
.x
= size_full_modified
.x
= window
->AutoFitOnlyGrows
? ImMax(window
->SizeFull
.x
, size_auto_fit
.x
) : size_auto_fit
.x
;
6401 if (!window_size_y_set_by_api
&& window
->AutoFitFramesY
> 0)
6402 window
->SizeFull
.y
= size_full_modified
.y
= window
->AutoFitOnlyGrows
? ImMax(window
->SizeFull
.y
, size_auto_fit
.y
) : size_auto_fit
.y
;
6403 if (!window
->Collapsed
)
6404 MarkIniSettingsDirty(window
);
6407 // Apply minimum/maximum window size constraints and final size
6408 window
->SizeFull
= CalcSizeAfterConstraint(window
, window
->SizeFull
);
6409 window
->Size
= window
->Collapsed
&& !(flags
& ImGuiWindowFlags_ChildWindow
) ? window
->TitleBarRect().GetSize() : window
->SizeFull
;
6413 // Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size).
6414 if (!window
->Collapsed
)
6416 // When reading the current size we need to read it after size constraints have been applied
6417 float size_x_for_scrollbars
= size_full_modified
.x
!= FLT_MAX
? window
->SizeFull
.x
: window
->SizeFullAtLastBegin
.x
;
6418 float size_y_for_scrollbars
= size_full_modified
.y
!= FLT_MAX
? window
->SizeFull
.y
: window
->SizeFullAtLastBegin
.y
;
6419 window
->ScrollbarY
= (flags
& ImGuiWindowFlags_AlwaysVerticalScrollbar
) || ((window
->SizeContents
.y
> size_y_for_scrollbars
) && !(flags
& ImGuiWindowFlags_NoScrollbar
));
6420 window
->ScrollbarX
= (flags
& ImGuiWindowFlags_AlwaysHorizontalScrollbar
) || ((window
->SizeContents
.x
> size_x_for_scrollbars
- (window
->ScrollbarY
? style
.ScrollbarSize
: 0.0f
)) && !(flags
& ImGuiWindowFlags_NoScrollbar
) && (flags
& ImGuiWindowFlags_HorizontalScrollbar
));
6421 if (window
->ScrollbarX
&& !window
->ScrollbarY
)
6422 window
->ScrollbarY
= (window
->SizeContents
.y
> size_y_for_scrollbars
- style
.ScrollbarSize
) && !(flags
& ImGuiWindowFlags_NoScrollbar
);
6423 window
->ScrollbarSizes
= ImVec2(window
->ScrollbarY
? style
.ScrollbarSize
: 0.0f
, window
->ScrollbarX
? style
.ScrollbarSize
: 0.0f
);
6428 // Popup latch its initial position, will position itself when it appears next frame
6429 if (window_just_activated_by_user
)
6431 window
->AutoPosLastDirection
= ImGuiDir_None
;
6432 if ((flags
& ImGuiWindowFlags_Popup
) != 0 && !window_pos_set_by_api
)
6433 window
->Pos
= g
.CurrentPopupStack
.back().OpenPopupPos
;
6436 // Position child window
6437 if (flags
& ImGuiWindowFlags_ChildWindow
)
6439 window
->BeginOrderWithinParent
= parent_window
->DC
.ChildWindows
.Size
;
6440 parent_window
->DC
.ChildWindows
.push_back(window
);
6441 if (!(flags
& ImGuiWindowFlags_Popup
) && !window_pos_set_by_api
&& !window_is_child_tooltip
)
6442 window
->Pos
= parent_window
->DC
.CursorPos
;
6445 const bool window_pos_with_pivot
= (window
->SetWindowPosVal
.x
!= FLT_MAX
&& window
->HiddenFramesForResize
== 0);
6446 if (window_pos_with_pivot
)
6447 SetWindowPos(window
, ImMax(style
.DisplaySafeAreaPadding
, window
->SetWindowPosVal
- window
->SizeFull
* window
->SetWindowPosPivot
), 0); // Position given a pivot (e.g. for centering)
6448 else if ((flags
& ImGuiWindowFlags_ChildMenu
) != 0)
6449 window
->Pos
= FindBestWindowPosForPopup(window
);
6450 else if ((flags
& ImGuiWindowFlags_Popup
) != 0 && !window_pos_set_by_api
&& window_just_appearing_after_hidden_for_resize
)
6451 window
->Pos
= FindBestWindowPosForPopup(window
);
6452 else if ((flags
& ImGuiWindowFlags_Tooltip
) != 0 && !window_pos_set_by_api
&& !window_is_child_tooltip
)
6453 window
->Pos
= FindBestWindowPosForPopup(window
);
6455 // Clamp position so it stays visible
6456 if (!(flags
& ImGuiWindowFlags_ChildWindow
))
6458 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.
6460 ImVec2 padding
= ImMax(style
.DisplayWindowPadding
, style
.DisplaySafeAreaPadding
);
6461 window
->Pos
= ImMax(window
->Pos
+ window
->Size
, padding
) - window
->Size
;
6462 window
->Pos
= ImMin(window
->Pos
, g
.IO
.DisplaySize
- padding
);
6465 window
->Pos
= ImFloor(window
->Pos
);
6467 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
6468 window
->WindowRounding
= (flags
& ImGuiWindowFlags_ChildWindow
) ? style
.ChildRounding
: ((flags
& ImGuiWindowFlags_Popup
) && !(flags
& ImGuiWindowFlags_Modal
)) ? style
.PopupRounding
: style
.WindowRounding
;
6470 // Prepare for item focus requests
6471 window
->FocusIdxAllRequestCurrent
= (window
->FocusIdxAllRequestNext
== INT_MAX
|| window
->FocusIdxAllCounter
== -1) ? INT_MAX
: (window
->FocusIdxAllRequestNext
+ (window
->FocusIdxAllCounter
+1)) % (window
->FocusIdxAllCounter
+1);
6472 window
->FocusIdxTabRequestCurrent
= (window
->FocusIdxTabRequestNext
== INT_MAX
|| window
->FocusIdxTabCounter
== -1) ? INT_MAX
: (window
->FocusIdxTabRequestNext
+ (window
->FocusIdxTabCounter
+1)) % (window
->FocusIdxTabCounter
+1);
6473 window
->FocusIdxAllCounter
= window
->FocusIdxTabCounter
= -1;
6474 window
->FocusIdxAllRequestNext
= window
->FocusIdxTabRequestNext
= INT_MAX
;
6477 window
->Scroll
= CalcNextScrollFromScrollTargetAndClamp(window
, true);
6478 window
->ScrollTarget
= ImVec2(FLT_MAX
, FLT_MAX
);
6480 // Apply window focus (new and reactivated windows are moved to front)
6481 bool want_focus
= false;
6482 if (window_just_activated_by_user
&& !(flags
& ImGuiWindowFlags_NoFocusOnAppearing
))
6483 if (!(flags
& (ImGuiWindowFlags_ChildWindow
| ImGuiWindowFlags_Tooltip
)) || (flags
& ImGuiWindowFlags_Popup
))
6486 // Handle manual resize: Resize Grips, Borders, Gamepad
6487 int border_held
= -1;
6488 ImU32 resize_grip_col
[4] = { 0 };
6489 const int resize_grip_count
= g
.IO
.OptResizeWindowsFromEdges
? 2 : 1; // 4
6490 const float grip_draw_size
= (float)(int)ImMax(g
.FontSize
* 1.35f
, window
->WindowRounding
+ 1.0f
+ g
.FontSize
* 0.2f
);
6491 if (!window
->Collapsed
)
6492 UpdateManualResize(window
, size_auto_fit
, &border_held
, resize_grip_count
, &resize_grip_col
[0]);
6494 // Default item width. Make it proportional to window size if window manually resizes
6495 if (window
->Size
.x
> 0.0f
&& !(flags
& ImGuiWindowFlags_Tooltip
) && !(flags
& ImGuiWindowFlags_AlwaysAutoResize
))
6496 window
->ItemWidthDefault
= (float)(int)(window
->Size
.x
* 0.65f
);
6498 window
->ItemWidthDefault
= (float)(int)(g
.FontSize
* 16.0f
);
6502 // Setup draw list and outer clipping rectangle
6503 window
->DrawList
->Clear();
6504 window
->DrawList
->Flags
= (g
.Style
.AntiAliasedLines
? ImDrawListFlags_AntiAliasedLines
: 0) | (g
.Style
.AntiAliasedFill
? ImDrawListFlags_AntiAliasedFill
: 0);
6505 window
->DrawList
->PushTextureID(g
.Font
->ContainerAtlas
->TexID
);
6506 ImRect
viewport_rect(GetViewportRect());
6507 if ((flags
& ImGuiWindowFlags_ChildWindow
) && !(flags
& ImGuiWindowFlags_Popup
) && !window_is_child_tooltip
)
6508 PushClipRect(parent_window
->ClipRect
.Min
, parent_window
->ClipRect
.Max
, true);
6510 PushClipRect(viewport_rect
.Min
, viewport_rect
.Max
, true);
6512 // Draw modal window background (darkens what is behind them, all viewports)
6513 const bool dim_bg_for_modal
= (flags
& ImGuiWindowFlags_Modal
) && window
== GetFrontMostPopupModal() && window
->HiddenFramesForResize
<= 0;
6514 const bool dim_bg_for_window_list
= g
.NavWindowingTargetAnim
&& (window
== g
.NavWindowingTargetAnim
->RootWindow
);
6515 if (dim_bg_for_modal
|| dim_bg_for_window_list
)
6517 const ImU32 dim_bg_col
= GetColorU32(dim_bg_for_modal
? ImGuiCol_ModalWindowDimBg
: ImGuiCol_NavWindowingDimBg
, g
.DimBgRatio
);
6518 window
->DrawList
->AddRectFilled(viewport_rect
.Min
, viewport_rect
.Max
, dim_bg_col
);
6521 // Draw navigation selection/windowing rectangle background
6522 if (dim_bg_for_window_list
&& window
== g
.NavWindowingTargetAnim
)
6524 ImRect bb
= window
->Rect();
6525 bb
.Expand(g
.FontSize
);
6526 if (!bb
.Contains(viewport_rect
)) // Avoid drawing if the window covers all the viewport anyway
6527 window
->DrawList
->AddRectFilled(bb
.Min
, bb
.Max
, GetColorU32(ImGuiCol_NavWindowingHighlight
, g
.NavWindowingHighlightAlpha
* 0.25f
), g
.Style
.WindowRounding
);
6530 // Draw window + handle manual resize
6531 const float window_rounding
= window
->WindowRounding
;
6532 const float window_border_size
= window
->WindowBorderSize
;
6533 const ImGuiWindow
* window_to_highlight
= g
.NavWindowingTarget
? g
.NavWindowingTarget
: g
.NavWindow
;
6534 const bool title_bar_is_highlight
= want_focus
|| (window_to_highlight
&& window
->RootWindowForTitleBarHighlight
== window_to_highlight
->RootWindowForTitleBarHighlight
);
6535 const ImRect title_bar_rect
= window
->TitleBarRect();
6536 if (window
->Collapsed
)
6539 float backup_border_size
= style
.FrameBorderSize
;
6540 g
.Style
.FrameBorderSize
= window
->WindowBorderSize
;
6541 ImU32 title_bar_col
= GetColorU32((title_bar_is_highlight
&& !g
.NavDisableHighlight
) ? ImGuiCol_TitleBgActive
: ImGuiCol_TitleBgCollapsed
);
6542 RenderFrame(title_bar_rect
.Min
, title_bar_rect
.Max
, title_bar_col
, true, window_rounding
);
6543 g
.Style
.FrameBorderSize
= backup_border_size
;
6547 // Window background
6548 ImU32 bg_col
= GetColorU32(GetWindowBgColorIdxFromFlags(flags
));
6549 if (g
.NextWindowData
.BgAlphaCond
!= 0)
6551 bg_col
= (bg_col
& ~IM_COL32_A_MASK
) | (IM_F32_TO_INT8_SAT(g
.NextWindowData
.BgAlphaVal
) << IM_COL32_A_SHIFT
);
6552 g
.NextWindowData
.BgAlphaCond
= 0;
6554 window
->DrawList
->AddRectFilled(window
->Pos
+ ImVec2(0, window
->TitleBarHeight()), window
->Pos
+ window
->Size
, bg_col
, window_rounding
, (flags
& ImGuiWindowFlags_NoTitleBar
) ? ImDrawCornerFlags_All
: ImDrawCornerFlags_Bot
);
6557 ImU32 title_bar_col
= GetColorU32(window
->Collapsed
? ImGuiCol_TitleBgCollapsed
: title_bar_is_highlight
? ImGuiCol_TitleBgActive
: ImGuiCol_TitleBg
);
6558 if (!(flags
& ImGuiWindowFlags_NoTitleBar
))
6559 window
->DrawList
->AddRectFilled(title_bar_rect
.Min
, title_bar_rect
.Max
, title_bar_col
, window_rounding
, ImDrawCornerFlags_Top
);
6562 if (flags
& ImGuiWindowFlags_MenuBar
)
6564 ImRect menu_bar_rect
= window
->MenuBarRect();
6565 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.
6566 window
->DrawList
->AddRectFilled(menu_bar_rect
.Min
, menu_bar_rect
.Max
, GetColorU32(ImGuiCol_MenuBarBg
), (flags
& ImGuiWindowFlags_NoTitleBar
) ? window_rounding
: 0.0f
, ImDrawCornerFlags_Top
);
6567 if (style
.FrameBorderSize
> 0.0f
&& menu_bar_rect
.Max
.y
< window
->Pos
.y
+ window
->Size
.y
)
6568 window
->DrawList
->AddLine(menu_bar_rect
.GetBL(), menu_bar_rect
.GetBR(), GetColorU32(ImGuiCol_Border
), style
.FrameBorderSize
);
6572 if (window
->ScrollbarX
)
6573 Scrollbar(ImGuiLayoutType_Horizontal
);
6574 if (window
->ScrollbarY
)
6575 Scrollbar(ImGuiLayoutType_Vertical
);
6577 // Render resize grips (after their input handling so we don't have a frame of latency)
6578 if (!(flags
& ImGuiWindowFlags_NoResize
))
6580 for (int resize_grip_n
= 0; resize_grip_n
< resize_grip_count
; resize_grip_n
++)
6582 const ImGuiResizeGripDef
& grip
= resize_grip_def
[resize_grip_n
];
6583 const ImVec2 corner
= ImLerp(window
->Pos
, window
->Pos
+ window
->Size
, grip
.CornerPos
);
6584 window
->DrawList
->PathLineTo(corner
+ grip
.InnerDir
* ((resize_grip_n
& 1) ? ImVec2(window_border_size
, grip_draw_size
) : ImVec2(grip_draw_size
, window_border_size
)));
6585 window
->DrawList
->PathLineTo(corner
+ grip
.InnerDir
* ((resize_grip_n
& 1) ? ImVec2(grip_draw_size
, window_border_size
) : ImVec2(window_border_size
, grip_draw_size
)));
6586 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
);
6587 window
->DrawList
->PathFillConvex(resize_grip_col
[resize_grip_n
]);
6592 if (window_border_size
> 0.0f
)
6593 window
->DrawList
->AddRect(window
->Pos
, window
->Pos
+ window
->Size
, GetColorU32(ImGuiCol_Border
), window_rounding
, ImDrawCornerFlags_All
, window_border_size
);
6594 if (border_held
!= -1)
6596 ImRect border
= GetBorderRect(window
, border_held
, grip_draw_size
, 0.0f
);
6597 window
->DrawList
->AddLine(border
.Min
, border
.Max
, GetColorU32(ImGuiCol_SeparatorActive
), ImMax(1.0f
, window_border_size
));
6599 if (style
.FrameBorderSize
> 0 && !(flags
& ImGuiWindowFlags_NoTitleBar
))
6600 window
->DrawList
->AddLine(title_bar_rect
.GetBL() + ImVec2(style
.WindowBorderSize
, -1), title_bar_rect
.GetBR() + ImVec2(-style
.WindowBorderSize
, -1), GetColorU32(ImGuiCol_Border
), style
.FrameBorderSize
);
6603 // Draw navigation selection/windowing rectangle border
6604 if (g
.NavWindowingTargetAnim
== window
)
6606 float rounding
= ImMax(window
->WindowRounding
, g
.Style
.WindowRounding
);
6607 ImRect bb
= window
->Rect();
6608 bb
.Expand(g
.FontSize
);
6609 if (bb
.Contains(viewport_rect
)) // If a window fits the entire viewport, adjust its highlight inward
6611 bb
.Expand(-g
.FontSize
- 1.0f
);
6612 rounding
= window
->WindowRounding
;
6614 window
->DrawList
->AddRect(bb
.Min
, bb
.Max
, GetColorU32(ImGuiCol_NavWindowingHighlight
, g
.NavWindowingHighlightAlpha
), rounding
, ~0, 3.0f
);
6617 // Store a backup of SizeFull which we will use next frame to decide if we need scrollbars.
6618 window
->SizeFullAtLastBegin
= window
->SizeFull
;
6620 // Update various regions. Variables they depends on are set above in this function.
6621 // FIXME: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
6622 window
->ContentsRegionRect
.Min
.x
= window
->Pos
.x
- window
->Scroll
.x
+ window
->WindowPadding
.x
;
6623 window
->ContentsRegionRect
.Min
.y
= window
->Pos
.y
- window
->Scroll
.y
+ window
->WindowPadding
.y
+ window
->TitleBarHeight() + window
->MenuBarHeight();
6624 window
->ContentsRegionRect
.Max
.x
= window
->Pos
.x
- window
->Scroll
.x
- window
->WindowPadding
.x
+ (window
->SizeContentsExplicit
.x
!= 0.0f
? window
->SizeContentsExplicit
.x
: (window
->Size
.x
- window
->ScrollbarSizes
.x
));
6625 window
->ContentsRegionRect
.Max
.y
= window
->Pos
.y
- window
->Scroll
.y
- window
->WindowPadding
.y
+ (window
->SizeContentsExplicit
.y
!= 0.0f
? window
->SizeContentsExplicit
.y
: (window
->Size
.y
- window
->ScrollbarSizes
.y
));
6627 // Setup drawing context
6628 // (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.)
6629 window
->DC
.IndentX
= 0.0f
+ window
->WindowPadding
.x
- window
->Scroll
.x
;
6630 window
->DC
.GroupOffsetX
= 0.0f
;
6631 window
->DC
.ColumnsOffsetX
= 0.0f
;
6632 window
->DC
.CursorStartPos
= window
->Pos
+ ImVec2(window
->DC
.IndentX
+ window
->DC
.ColumnsOffsetX
, window
->TitleBarHeight() + window
->MenuBarHeight() + window
->WindowPadding
.y
- window
->Scroll
.y
);
6633 window
->DC
.CursorPos
= window
->DC
.CursorStartPos
;
6634 window
->DC
.CursorPosPrevLine
= window
->DC
.CursorPos
;
6635 window
->DC
.CursorMaxPos
= window
->DC
.CursorStartPos
;
6636 window
->DC
.CurrentLineHeight
= window
->DC
.PrevLineHeight
= 0.0f
;
6637 window
->DC
.CurrentLineTextBaseOffset
= window
->DC
.PrevLineTextBaseOffset
= 0.0f
;
6638 window
->DC
.NavHideHighlightOneFrame
= false;
6639 window
->DC
.NavHasScroll
= (GetScrollMaxY() > 0.0f
);
6640 window
->DC
.NavLayerActiveMask
= window
->DC
.NavLayerActiveMaskNext
;
6641 window
->DC
.NavLayerActiveMaskNext
= 0x00;
6642 window
->DC
.MenuBarAppending
= false;
6643 window
->DC
.LogLinePosY
= window
->DC
.CursorPos
.y
- 9999.0f
;
6644 window
->DC
.ChildWindows
.resize(0);
6645 window
->DC
.LayoutType
= ImGuiLayoutType_Vertical
;
6646 window
->DC
.ParentLayoutType
= parent_window
? parent_window
->DC
.LayoutType
: ImGuiLayoutType_Vertical
;
6647 window
->DC
.ItemFlags
= parent_window
? parent_window
->DC
.ItemFlags
: ImGuiItemFlags_Default_
;
6648 window
->DC
.ItemWidth
= window
->ItemWidthDefault
;
6649 window
->DC
.TextWrapPos
= -1.0f
; // disabled
6650 window
->DC
.ItemFlagsStack
.resize(0);
6651 window
->DC
.ItemWidthStack
.resize(0);
6652 window
->DC
.TextWrapPosStack
.resize(0);
6653 window
->DC
.ColumnsSet
= NULL
;
6654 window
->DC
.TreeDepth
= 0;
6655 window
->DC
.TreeDepthMayJumpToParentOnPop
= 0x00;
6656 window
->DC
.StateStorage
= &window
->StateStorage
;
6657 window
->DC
.GroupStack
.resize(0);
6658 window
->MenuColumns
.Update(3, style
.ItemSpacing
.x
, window_just_activated_by_user
);
6660 if ((flags
& ImGuiWindowFlags_ChildWindow
) && (window
->DC
.ItemFlags
!= parent_window
->DC
.ItemFlags
))
6662 window
->DC
.ItemFlags
= parent_window
->DC
.ItemFlags
;
6663 window
->DC
.ItemFlagsStack
.push_back(window
->DC
.ItemFlags
);
6666 if (window
->AutoFitFramesX
> 0)
6667 window
->AutoFitFramesX
--;
6668 if (window
->AutoFitFramesY
> 0)
6669 window
->AutoFitFramesY
--;
6671 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
6674 FocusWindow(window
);
6675 NavInitWindow(window
, false);
6679 if (!(flags
& ImGuiWindowFlags_NoTitleBar
))
6681 // Close & collapse button are on layer 1 (same as menus) and don't default focus
6682 const ImGuiItemFlags item_flags_backup
= window
->DC
.ItemFlags
;
6683 window
->DC
.ItemFlags
|= ImGuiItemFlags_NoNavDefaultFocus
;
6684 window
->DC
.NavLayerCurrent
++;
6685 window
->DC
.NavLayerCurrentMask
<<= 1;
6688 if (!(flags
& ImGuiWindowFlags_NoCollapse
))
6689 if (CollapseButton(window
->GetID("#COLLAPSE"), window
->Pos
+ style
.FramePadding
))
6690 window
->CollapseToggleWanted
= true; // Defer collapsing to next frame as we are too far in the Begin() function
6695 const float pad
= style
.FramePadding
.y
;
6696 const float rad
= g
.FontSize
* 0.5f
;
6697 if (CloseButton(window
->GetID("#CLOSE"), window
->Rect().GetTR() + ImVec2(-pad
- rad
, pad
+ rad
), rad
+ 1))
6701 window
->DC
.NavLayerCurrent
--;
6702 window
->DC
.NavLayerCurrentMask
>>= 1;
6703 window
->DC
.ItemFlags
= item_flags_backup
;
6705 // Title text (FIXME: refactor text alignment facilities along with RenderText helpers, this is too much code for what it does.)
6706 ImVec2 text_size
= CalcTextSize(name
, NULL
, true);
6707 ImRect text_r
= title_bar_rect
;
6708 float pad_left
= (flags
& ImGuiWindowFlags_NoCollapse
) ? style
.FramePadding
.x
: (style
.FramePadding
.x
+ g
.FontSize
+ style
.ItemInnerSpacing
.x
);
6709 float pad_right
= (p_open
== NULL
) ? style
.FramePadding
.x
: (style
.FramePadding
.x
+ g
.FontSize
+ style
.ItemInnerSpacing
.x
);
6710 if (style
.WindowTitleAlign
.x
> 0.0f
)
6711 pad_right
= ImLerp(pad_right
, pad_left
, style
.WindowTitleAlign
.x
);
6712 text_r
.Min
.x
+= pad_left
;
6713 text_r
.Max
.x
-= pad_right
;
6714 ImRect clip_rect
= text_r
;
6715 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()
6716 RenderTextClipped(text_r
.Min
, text_r
.Max
, name
, NULL
, &text_size
, style
.WindowTitleAlign
, &clip_rect
);
6719 // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
6720 window
->OuterRectClipped
= window
->Rect();
6721 window
->OuterRectClipped
.ClipWith(window
->ClipRect
);
6723 // Pressing CTRL+C while holding on a window copy its content to the clipboard
6724 // 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.
6725 // Maybe we can support CTRL+C on every element?
6727 if (g.ActiveId == move_id)
6728 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
6729 ImGui::LogToClipboard();
6733 // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
6734 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
6735 window
->InnerMainRect
.Min
.x
= title_bar_rect
.Min
.x
+ window
->WindowBorderSize
;
6736 window
->InnerMainRect
.Min
.y
= title_bar_rect
.Max
.y
+ window
->MenuBarHeight() + (((flags
& ImGuiWindowFlags_MenuBar
) || !(flags
& ImGuiWindowFlags_NoTitleBar
)) ? style
.FrameBorderSize
: window
->WindowBorderSize
);
6737 window
->InnerMainRect
.Max
.x
= window
->Pos
.x
+ window
->Size
.x
- window
->ScrollbarSizes
.x
- window
->WindowBorderSize
;
6738 window
->InnerMainRect
.Max
.y
= window
->Pos
.y
+ window
->Size
.y
- window
->ScrollbarSizes
.y
- window
->WindowBorderSize
;
6739 //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
6741 // Inner clipping rectangle
6742 // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
6743 window
->InnerClipRect
.Min
.x
= ImFloor(0.5f
+ window
->InnerMainRect
.Min
.x
+ ImMax(0.0f
, ImFloor(window
->WindowPadding
.x
*0.5f
- window
->WindowBorderSize
)));
6744 window
->InnerClipRect
.Min
.y
= ImFloor(0.5f
+ window
->InnerMainRect
.Min
.y
);
6745 window
->InnerClipRect
.Max
.x
= ImFloor(0.5f
+ window
->InnerMainRect
.Max
.x
- ImMax(0.0f
, ImFloor(window
->WindowPadding
.x
*0.5f
- window
->WindowBorderSize
)));
6746 window
->InnerClipRect
.Max
.y
= ImFloor(0.5f
+ window
->InnerMainRect
.Max
.y
);
6748 // 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.).
6749 window
->DC
.LastItemId
= window
->MoveId
;
6750 window
->DC
.LastItemStatusFlags
= IsMouseHoveringRect(title_bar_rect
.Min
, title_bar_rect
.Max
, false) ? ImGuiItemStatusFlags_HoveredRect
: 0;
6751 window
->DC
.LastItemRect
= title_bar_rect
;
6754 PushClipRect(window
->InnerClipRect
.Min
, window
->InnerClipRect
.Max
, true);
6756 // 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)
6757 if (first_begin_of_the_frame
)
6758 window
->WriteAccessed
= false;
6760 window
->BeginCount
++;
6761 g
.NextWindowData
.Clear();
6763 if (flags
& ImGuiWindowFlags_ChildWindow
)
6765 // Child window can be out of sight and have "negative" clip windows.
6766 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
6767 IM_ASSERT((flags
& ImGuiWindowFlags_NoTitleBar
) != 0);
6769 if (!(flags
& ImGuiWindowFlags_AlwaysAutoResize
) && window
->AutoFitFramesX
<= 0 && window
->AutoFitFramesY
<= 0)
6770 if (window
->OuterRectClipped
.Min
.x
>= window
->OuterRectClipped
.Max
.x
|| window
->OuterRectClipped
.Min
.y
>= window
->OuterRectClipped
.Max
.y
)
6771 window
->HiddenFramesRegular
= 1;
6773 // Completely hide along with parent or if parent is collapsed
6774 if (parent_window
&& (parent_window
->Collapsed
|| parent_window
->Hidden
))
6775 window
->HiddenFramesRegular
= 1;
6778 // 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)
6779 if (style
.Alpha
<= 0.0f
)
6780 window
->HiddenFramesRegular
= 1;
6782 // Update the Hidden flag
6783 window
->Hidden
= (window
->HiddenFramesRegular
> 0) || (window
->HiddenFramesForResize
);
6785 // Return false if we don't intend to display anything to allow user to perform an early out optimization
6786 window
->SkipItems
= (window
->Collapsed
|| !window
->Active
|| window
->Hidden
) && window
->AutoFitFramesX
<= 0 && window
->AutoFitFramesY
<= 0 && window
->HiddenFramesForResize
<= 0;
6788 return !window
->SkipItems
;
6791 // Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()/SetNextWindowBgAlpha() + Begin() instead.
6792 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
6793 bool ImGui::Begin(const char* name
, bool* p_open
, const ImVec2
& size_first_use
, float bg_alpha_override
, ImGuiWindowFlags flags
)
6795 // 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.
6796 if (size_first_use
.x
!= 0.0f
|| size_first_use
.y
!= 0.0f
)
6797 ImGui::SetNextWindowSize(size_first_use
, ImGuiCond_FirstUseEver
);
6799 // Old API feature: override the window background alpha with a parameter.
6800 if (bg_alpha_override
>= 0.0f
)
6801 ImGui::SetNextWindowBgAlpha(bg_alpha_override
);
6803 return ImGui::Begin(name
, p_open
, flags
);
6805 #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
6809 ImGuiContext
& g
= *GImGui
;
6810 ImGuiWindow
* window
= g
.CurrentWindow
;
6812 if (window
->DC
.ColumnsSet
!= NULL
)
6814 PopClipRect(); // Inner window clip rectangle
6817 if (!(window
->Flags
& ImGuiWindowFlags_ChildWindow
)) // FIXME: add more options for scope of logging
6820 // Pop from window stack
6821 g
.CurrentWindowStack
.pop_back();
6822 if (window
->Flags
& ImGuiWindowFlags_Popup
)
6823 g
.CurrentPopupStack
.pop_back();
6824 CheckStacksSize(window
, false);
6825 SetCurrentWindow(g
.CurrentWindowStack
.empty() ? NULL
: g
.CurrentWindowStack
.back());
6828 // Vertical scrollbar
6829 // The entire piece of code below is rather confusing because:
6830 // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
6831 // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar
6832 // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.
6833 void ImGui::Scrollbar(ImGuiLayoutType direction
)
6835 ImGuiContext
& g
= *GImGui
;
6836 ImGuiWindow
* window
= g
.CurrentWindow
;
6838 const bool horizontal
= (direction
== ImGuiLayoutType_Horizontal
);
6839 const ImGuiStyle
& style
= g
.Style
;
6840 const ImGuiID id
= window
->GetID(horizontal
? "#SCROLLX" : "#SCROLLY");
6842 // Render background
6843 bool other_scrollbar
= (horizontal
? window
->ScrollbarY
: window
->ScrollbarX
);
6844 float other_scrollbar_size_w
= other_scrollbar
? style
.ScrollbarSize
: 0.0f
;
6845 const ImRect window_rect
= window
->Rect();
6846 const float border_size
= window
->WindowBorderSize
;
6847 ImRect bb
= horizontal
6848 ? 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
)
6849 : 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
);
6851 bb
.Min
.y
+= window
->TitleBarHeight() + ((window
->Flags
& ImGuiWindowFlags_MenuBar
) ? window
->MenuBarHeight() : 0.0f
);
6852 if (bb
.GetWidth() <= 0.0f
|| bb
.GetHeight() <= 0.0f
)
6855 int window_rounding_corners
;
6857 window_rounding_corners
= ImDrawCornerFlags_BotLeft
| (other_scrollbar
? 0 : ImDrawCornerFlags_BotRight
);
6859 window_rounding_corners
= (((window
->Flags
& ImGuiWindowFlags_NoTitleBar
) && !(window
->Flags
& ImGuiWindowFlags_MenuBar
)) ? ImDrawCornerFlags_TopRight
: 0) | (other_scrollbar
? 0 : ImDrawCornerFlags_BotRight
);
6860 window
->DrawList
->AddRectFilled(bb
.Min
, bb
.Max
, GetColorU32(ImGuiCol_ScrollbarBg
), window
->WindowRounding
, window_rounding_corners
);
6861 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
)));
6863 // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
6864 float scrollbar_size_v
= horizontal
? bb
.GetWidth() : bb
.GetHeight();
6865 float scroll_v
= horizontal
? window
->Scroll
.x
: window
->Scroll
.y
;
6866 float win_size_avail_v
= (horizontal
? window
->SizeFull
.x
: window
->SizeFull
.y
) - other_scrollbar_size_w
;
6867 float win_size_contents_v
= horizontal
? window
->SizeContents
.x
: window
->SizeContents
.y
;
6869 // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount)
6870 // But we maintain a minimum size in pixel to allow for the user to still aim inside.
6871 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.
6872 const float win_size_v
= ImMax(ImMax(win_size_contents_v
, win_size_avail_v
), 1.0f
);
6873 const float grab_h_pixels
= ImClamp(scrollbar_size_v
* (win_size_avail_v
/ win_size_v
), style
.GrabMinSize
, scrollbar_size_v
);
6874 const float grab_h_norm
= grab_h_pixels
/ scrollbar_size_v
;
6876 // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
6878 bool hovered
= false;
6879 const bool previously_held
= (g
.ActiveId
== id
);
6880 ButtonBehavior(bb
, id
, &hovered
, &held
, ImGuiButtonFlags_NoNavFocus
);
6882 float scroll_max
= ImMax(1.0f
, win_size_contents_v
- win_size_avail_v
);
6883 float scroll_ratio
= ImSaturate(scroll_v
/ scroll_max
);
6884 float grab_v_norm
= scroll_ratio
* (scrollbar_size_v
- grab_h_pixels
) / scrollbar_size_v
;
6885 if (held
&& grab_h_norm
< 1.0f
)
6887 float scrollbar_pos_v
= horizontal
? bb
.Min
.x
: bb
.Min
.y
;
6888 float mouse_pos_v
= horizontal
? g
.IO
.MousePos
.x
: g
.IO
.MousePos
.y
;
6889 float* click_delta_to_grab_center_v
= horizontal
? &g
.ScrollbarClickDeltaToGrabCenter
.x
: &g
.ScrollbarClickDeltaToGrabCenter
.y
;
6891 // Click position in scrollbar normalized space (0.0f->1.0f)
6892 const float clicked_v_norm
= ImSaturate((mouse_pos_v
- scrollbar_pos_v
) / scrollbar_size_v
);
6895 bool seek_absolute
= false;
6896 if (!previously_held
)
6898 // On initial click calculate the distance between mouse and the center of the grab
6899 if (clicked_v_norm
>= grab_v_norm
&& clicked_v_norm
<= grab_v_norm
+ grab_h_norm
)
6901 *click_delta_to_grab_center_v
= clicked_v_norm
- grab_v_norm
- grab_h_norm
*0.5f
;
6905 seek_absolute
= true;
6906 *click_delta_to_grab_center_v
= 0.0f
;
6911 // 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
6912 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
));
6913 scroll_v
= (float)(int)(0.5f
+ scroll_v_norm
* scroll_max
);//(win_size_contents_v - win_size_v));
6915 window
->Scroll
.x
= scroll_v
;
6917 window
->Scroll
.y
= scroll_v
;
6919 // Update values for rendering
6920 scroll_ratio
= ImSaturate(scroll_v
/ scroll_max
);
6921 grab_v_norm
= scroll_ratio
* (scrollbar_size_v
- grab_h_pixels
) / scrollbar_size_v
;
6923 // Update distance to grab now that we have seeked and saturated
6925 *click_delta_to_grab_center_v
= clicked_v_norm
- grab_v_norm
- grab_h_norm
*0.5f
;
6929 const ImU32 grab_col
= GetColorU32(held
? ImGuiCol_ScrollbarGrabActive
: hovered
? ImGuiCol_ScrollbarGrabHovered
: ImGuiCol_ScrollbarGrab
);
6932 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
);
6934 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
));
6935 window
->DrawList
->AddRectFilled(grab_rect
.Min
, grab_rect
.Max
, grab_col
, style
.ScrollbarRounding
);
6938 void ImGui::BringWindowToFront(ImGuiWindow
* window
)
6940 ImGuiContext
& g
= *GImGui
;
6941 ImGuiWindow
* current_front_window
= g
.Windows
.back();
6942 if (current_front_window
== window
|| current_front_window
->RootWindow
== window
)
6944 for (int i
= g
.Windows
.Size
- 2; i
>= 0; i
--) // We can ignore the front most window
6945 if (g
.Windows
[i
] == window
)
6947 g
.Windows
.erase(g
.Windows
.Data
+ i
);
6948 g
.Windows
.push_back(window
);
6953 void ImGui::BringWindowToBack(ImGuiWindow
* window
)
6955 ImGuiContext
& g
= *GImGui
;
6956 if (g
.Windows
[0] == window
)
6958 for (int i
= 0; i
< g
.Windows
.Size
; i
++)
6959 if (g
.Windows
[i
] == window
)
6961 memmove(&g
.Windows
[1], &g
.Windows
[0], (size_t)i
* sizeof(ImGuiWindow
*));
6962 g
.Windows
[0] = window
;
6967 // Moving window to front of display and set focus (which happens to be back of our sorted list)
6968 void ImGui::FocusWindow(ImGuiWindow
* window
)
6970 ImGuiContext
& g
= *GImGui
;
6972 if (g
.NavWindow
!= window
)
6974 g
.NavWindow
= window
;
6975 if (window
&& g
.NavDisableMouseHover
)
6976 g
.NavMousePosDirty
= true;
6977 g
.NavInitRequest
= false;
6978 g
.NavId
= window
? window
->NavLastIds
[0] : 0; // Restore NavId
6979 g
.NavIdIsAlive
= false;
6981 //printf("[%05d] FocusWindow(\"%s\")\n", g.FrameCount, window ? window->Name : NULL);
6984 // Passing NULL allow to disable keyboard focus
6988 // Move the root window to the top of the pile
6989 if (window
->RootWindow
)
6990 window
= window
->RootWindow
;
6992 // Steal focus on active widgets
6993 if (window
->Flags
& ImGuiWindowFlags_Popup
) // FIXME: This statement should be unnecessary. Need further testing before removing it..
6994 if (g
.ActiveId
!= 0 && g
.ActiveIdWindow
&& g
.ActiveIdWindow
->RootWindow
!= window
)
6998 if (!(window
->Flags
& ImGuiWindowFlags_NoBringToFrontOnFocus
))
6999 BringWindowToFront(window
);
7002 void ImGui::FocusFrontMostActiveWindow(ImGuiWindow
* ignore_window
)
7004 ImGuiContext
& g
= *GImGui
;
7005 for (int i
= g
.Windows
.Size
- 1; i
>= 0; i
--)
7006 if (g
.Windows
[i
] != ignore_window
&& g
.Windows
[i
]->WasActive
&& !(g
.Windows
[i
]->Flags
& ImGuiWindowFlags_ChildWindow
))
7008 ImGuiWindow
* focus_window
= NavRestoreLastChildNavWindow(g
.Windows
[i
]);
7009 FocusWindow(focus_window
);
7014 void ImGui::PushItemWidth(float item_width
)
7016 ImGuiWindow
* window
= GetCurrentWindow();
7017 window
->DC
.ItemWidth
= (item_width
== 0.0f
? window
->ItemWidthDefault
: item_width
);
7018 window
->DC
.ItemWidthStack
.push_back(window
->DC
.ItemWidth
);
7021 void ImGui::PushMultiItemsWidths(int components
, float w_full
)
7023 ImGuiWindow
* window
= GetCurrentWindow();
7024 const ImGuiStyle
& style
= GImGui
->Style
;
7026 w_full
= CalcItemWidth();
7027 const float w_item_one
= ImMax(1.0f
, (float)(int)((w_full
- (style
.ItemInnerSpacing
.x
) * (components
-1)) / (float)components
));
7028 const float w_item_last
= ImMax(1.0f
, (float)(int)(w_full
- (w_item_one
+ style
.ItemInnerSpacing
.x
) * (components
-1)));
7029 window
->DC
.ItemWidthStack
.push_back(w_item_last
);
7030 for (int i
= 0; i
< components
-1; i
++)
7031 window
->DC
.ItemWidthStack
.push_back(w_item_one
);
7032 window
->DC
.ItemWidth
= window
->DC
.ItemWidthStack
.back();
7035 void ImGui::PopItemWidth()
7037 ImGuiWindow
* window
= GetCurrentWindow();
7038 window
->DC
.ItemWidthStack
.pop_back();
7039 window
->DC
.ItemWidth
= window
->DC
.ItemWidthStack
.empty() ? window
->ItemWidthDefault
: window
->DC
.ItemWidthStack
.back();
7042 float ImGui::CalcItemWidth()
7044 ImGuiWindow
* window
= GetCurrentWindowRead();
7045 float w
= window
->DC
.ItemWidth
;
7048 // 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.
7049 float width_to_right_edge
= GetContentRegionAvail().x
;
7050 w
= ImMax(1.0f
, width_to_right_edge
+ w
);
7056 void ImGui::SetCurrentFont(ImFont
* font
)
7058 ImGuiContext
& g
= *GImGui
;
7059 IM_ASSERT(font
&& font
->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
7060 IM_ASSERT(font
->Scale
> 0.0f
);
7062 g
.FontBaseSize
= g
.IO
.FontGlobalScale
* g
.Font
->FontSize
* g
.Font
->Scale
;
7063 g
.FontSize
= g
.CurrentWindow
? g
.CurrentWindow
->CalcFontSize() : 0.0f
;
7065 ImFontAtlas
* atlas
= g
.Font
->ContainerAtlas
;
7066 g
.DrawListSharedData
.TexUvWhitePixel
= atlas
->TexUvWhitePixel
;
7067 g
.DrawListSharedData
.Font
= g
.Font
;
7068 g
.DrawListSharedData
.FontSize
= g
.FontSize
;
7071 void ImGui::PushFont(ImFont
* font
)
7073 ImGuiContext
& g
= *GImGui
;
7075 font
= GetDefaultFont();
7076 SetCurrentFont(font
);
7077 g
.FontStack
.push_back(font
);
7078 g
.CurrentWindow
->DrawList
->PushTextureID(font
->ContainerAtlas
->TexID
);
7081 void ImGui::PopFont()
7083 ImGuiContext
& g
= *GImGui
;
7084 g
.CurrentWindow
->DrawList
->PopTextureID();
7085 g
.FontStack
.pop_back();
7086 SetCurrentFont(g
.FontStack
.empty() ? GetDefaultFont() : g
.FontStack
.back());
7089 void ImGui::PushItemFlag(ImGuiItemFlags option
, bool enabled
)
7091 ImGuiWindow
* window
= GetCurrentWindow();
7093 window
->DC
.ItemFlags
|= option
;
7095 window
->DC
.ItemFlags
&= ~option
;
7096 window
->DC
.ItemFlagsStack
.push_back(window
->DC
.ItemFlags
);
7099 void ImGui::PopItemFlag()
7101 ImGuiWindow
* window
= GetCurrentWindow();
7102 window
->DC
.ItemFlagsStack
.pop_back();
7103 window
->DC
.ItemFlags
= window
->DC
.ItemFlagsStack
.empty() ? ImGuiItemFlags_Default_
: window
->DC
.ItemFlagsStack
.back();
7106 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus
)
7108 PushItemFlag(ImGuiItemFlags_AllowKeyboardFocus
, allow_keyboard_focus
);
7111 void ImGui::PopAllowKeyboardFocus()
7116 void ImGui::PushButtonRepeat(bool repeat
)
7118 PushItemFlag(ImGuiItemFlags_ButtonRepeat
, repeat
);
7121 void ImGui::PopButtonRepeat()
7126 void ImGui::PushTextWrapPos(float wrap_pos_x
)
7128 ImGuiWindow
* window
= GetCurrentWindow();
7129 window
->DC
.TextWrapPos
= wrap_pos_x
;
7130 window
->DC
.TextWrapPosStack
.push_back(wrap_pos_x
);
7133 void ImGui::PopTextWrapPos()
7135 ImGuiWindow
* window
= GetCurrentWindow();
7136 window
->DC
.TextWrapPosStack
.pop_back();
7137 window
->DC
.TextWrapPos
= window
->DC
.TextWrapPosStack
.empty() ? -1.0f
: window
->DC
.TextWrapPosStack
.back();
7140 // 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
7141 void ImGui::PushStyleColor(ImGuiCol idx
, ImU32 col
)
7143 ImGuiContext
& g
= *GImGui
;
7146 backup
.BackupValue
= g
.Style
.Colors
[idx
];
7147 g
.ColorModifiers
.push_back(backup
);
7148 g
.Style
.Colors
[idx
] = ColorConvertU32ToFloat4(col
);
7151 void ImGui::PushStyleColor(ImGuiCol idx
, const ImVec4
& col
)
7153 ImGuiContext
& g
= *GImGui
;
7156 backup
.BackupValue
= g
.Style
.Colors
[idx
];
7157 g
.ColorModifiers
.push_back(backup
);
7158 g
.Style
.Colors
[idx
] = col
;
7161 void ImGui::PopStyleColor(int count
)
7163 ImGuiContext
& g
= *GImGui
;
7166 ImGuiColMod
& backup
= g
.ColorModifiers
.back();
7167 g
.Style
.Colors
[backup
.Col
] = backup
.BackupValue
;
7168 g
.ColorModifiers
.pop_back();
7173 struct ImGuiStyleVarInfo
7178 void* GetVarPtr(ImGuiStyle
* style
) const { return (void*)((unsigned char*)style
+ Offset
); }
7181 static const ImGuiStyleVarInfo GStyleVarInfo
[] =
7183 { ImGuiDataType_Float
, 1, (ImU32
)IM_OFFSETOF(ImGuiStyle
, Alpha
) }, // ImGuiStyleVar_Alpha
7184 { ImGuiDataType_Float
, 2, (ImU32
)IM_OFFSETOF(ImGuiStyle
, WindowPadding
) }, // ImGuiStyleVar_WindowPadding
7185 { ImGuiDataType_Float
, 1, (ImU32
)IM_OFFSETOF(ImGuiStyle
, WindowRounding
) }, // ImGuiStyleVar_WindowRounding
7186 { ImGuiDataType_Float
, 1, (ImU32
)IM_OFFSETOF(ImGuiStyle
, WindowBorderSize
) }, // ImGuiStyleVar_WindowBorderSize
7187 { ImGuiDataType_Float
, 2, (ImU32
)IM_OFFSETOF(ImGuiStyle
, WindowMinSize
) }, // ImGuiStyleVar_WindowMinSize
7188 { ImGuiDataType_Float
, 2, (ImU32
)IM_OFFSETOF(ImGuiStyle
, WindowTitleAlign
) }, // ImGuiStyleVar_WindowTitleAlign
7189 { ImGuiDataType_Float
, 1, (ImU32
)IM_OFFSETOF(ImGuiStyle
, ChildRounding
) }, // ImGuiStyleVar_ChildRounding
7190 { ImGuiDataType_Float
, 1, (ImU32
)IM_OFFSETOF(ImGuiStyle
, ChildBorderSize
) }, // ImGuiStyleVar_ChildBorderSize
7191 { ImGuiDataType_Float
, 1, (ImU32
)IM_OFFSETOF(ImGuiStyle
, PopupRounding
) }, // ImGuiStyleVar_PopupRounding
7192 { ImGuiDataType_Float
, 1, (ImU32
)IM_OFFSETOF(ImGuiStyle
, PopupBorderSize
) }, // ImGuiStyleVar_PopupBorderSize
7193 { ImGuiDataType_Float
, 2, (ImU32
)IM_OFFSETOF(ImGuiStyle
, FramePadding
) }, // ImGuiStyleVar_FramePadding
7194 { ImGuiDataType_Float
, 1, (ImU32
)IM_OFFSETOF(ImGuiStyle
, FrameRounding
) }, // ImGuiStyleVar_FrameRounding
7195 { ImGuiDataType_Float
, 1, (ImU32
)IM_OFFSETOF(ImGuiStyle
, FrameBorderSize
) }, // ImGuiStyleVar_FrameBorderSize
7196 { ImGuiDataType_Float
, 2, (ImU32
)IM_OFFSETOF(ImGuiStyle
, ItemSpacing
) }, // ImGuiStyleVar_ItemSpacing
7197 { ImGuiDataType_Float
, 2, (ImU32
)IM_OFFSETOF(ImGuiStyle
, ItemInnerSpacing
) }, // ImGuiStyleVar_ItemInnerSpacing
7198 { ImGuiDataType_Float
, 1, (ImU32
)IM_OFFSETOF(ImGuiStyle
, IndentSpacing
) }, // ImGuiStyleVar_IndentSpacing
7199 { ImGuiDataType_Float
, 1, (ImU32
)IM_OFFSETOF(ImGuiStyle
, ScrollbarSize
) }, // ImGuiStyleVar_ScrollbarSize
7200 { ImGuiDataType_Float
, 1, (ImU32
)IM_OFFSETOF(ImGuiStyle
, ScrollbarRounding
) }, // ImGuiStyleVar_ScrollbarRounding
7201 { ImGuiDataType_Float
, 1, (ImU32
)IM_OFFSETOF(ImGuiStyle
, GrabMinSize
) }, // ImGuiStyleVar_GrabMinSize
7202 { ImGuiDataType_Float
, 1, (ImU32
)IM_OFFSETOF(ImGuiStyle
, GrabRounding
) }, // ImGuiStyleVar_GrabRounding
7203 { ImGuiDataType_Float
, 2, (ImU32
)IM_OFFSETOF(ImGuiStyle
, ButtonTextAlign
) }, // ImGuiStyleVar_ButtonTextAlign
7206 static const ImGuiStyleVarInfo
* GetStyleVarInfo(ImGuiStyleVar idx
)
7208 IM_ASSERT(idx
>= 0 && idx
< ImGuiStyleVar_COUNT
);
7209 IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo
) == ImGuiStyleVar_COUNT
);
7210 return &GStyleVarInfo
[idx
];
7213 void ImGui::PushStyleVar(ImGuiStyleVar idx
, float val
)
7215 const ImGuiStyleVarInfo
* var_info
= GetStyleVarInfo(idx
);
7216 if (var_info
->Type
== ImGuiDataType_Float
&& var_info
->Count
== 1)
7218 ImGuiContext
& g
= *GImGui
;
7219 float* pvar
= (float*)var_info
->GetVarPtr(&g
.Style
);
7220 g
.StyleModifiers
.push_back(ImGuiStyleMod(idx
, *pvar
));
7224 IM_ASSERT(0); // Called function with wrong-type? Variable is not a float.
7227 void ImGui::PushStyleVar(ImGuiStyleVar idx
, const ImVec2
& val
)
7229 const ImGuiStyleVarInfo
* var_info
= GetStyleVarInfo(idx
);
7230 if (var_info
->Type
== ImGuiDataType_Float
&& var_info
->Count
== 2)
7232 ImGuiContext
& g
= *GImGui
;
7233 ImVec2
* pvar
= (ImVec2
*)var_info
->GetVarPtr(&g
.Style
);
7234 g
.StyleModifiers
.push_back(ImGuiStyleMod(idx
, *pvar
));
7238 IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2.
7241 void ImGui::PopStyleVar(int count
)
7243 ImGuiContext
& g
= *GImGui
;
7246 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
7247 ImGuiStyleMod
& backup
= g
.StyleModifiers
.back();
7248 const ImGuiStyleVarInfo
* info
= GetStyleVarInfo(backup
.VarIdx
);
7249 void* data
= info
->GetVarPtr(&g
.Style
);
7250 if (info
->Type
== ImGuiDataType_Float
&& info
->Count
== 1) { ((float*)data
)[0] = backup
.BackupFloat
[0]; }
7251 else if (info
->Type
== ImGuiDataType_Float
&& info
->Count
== 2) { ((float*)data
)[0] = backup
.BackupFloat
[0]; ((float*)data
)[1] = backup
.BackupFloat
[1]; }
7252 g
.StyleModifiers
.pop_back();
7257 const char* ImGui::GetStyleColorName(ImGuiCol idx
)
7259 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
7262 case ImGuiCol_Text
: return "Text";
7263 case ImGuiCol_TextDisabled
: return "TextDisabled";
7264 case ImGuiCol_WindowBg
: return "WindowBg";
7265 case ImGuiCol_ChildBg
: return "ChildBg";
7266 case ImGuiCol_PopupBg
: return "PopupBg";
7267 case ImGuiCol_Border
: return "Border";
7268 case ImGuiCol_BorderShadow
: return "BorderShadow";
7269 case ImGuiCol_FrameBg
: return "FrameBg";
7270 case ImGuiCol_FrameBgHovered
: return "FrameBgHovered";
7271 case ImGuiCol_FrameBgActive
: return "FrameBgActive";
7272 case ImGuiCol_TitleBg
: return "TitleBg";
7273 case ImGuiCol_TitleBgActive
: return "TitleBgActive";
7274 case ImGuiCol_TitleBgCollapsed
: return "TitleBgCollapsed";
7275 case ImGuiCol_MenuBarBg
: return "MenuBarBg";
7276 case ImGuiCol_ScrollbarBg
: return "ScrollbarBg";
7277 case ImGuiCol_ScrollbarGrab
: return "ScrollbarGrab";
7278 case ImGuiCol_ScrollbarGrabHovered
: return "ScrollbarGrabHovered";
7279 case ImGuiCol_ScrollbarGrabActive
: return "ScrollbarGrabActive";
7280 case ImGuiCol_CheckMark
: return "CheckMark";
7281 case ImGuiCol_SliderGrab
: return "SliderGrab";
7282 case ImGuiCol_SliderGrabActive
: return "SliderGrabActive";
7283 case ImGuiCol_Button
: return "Button";
7284 case ImGuiCol_ButtonHovered
: return "ButtonHovered";
7285 case ImGuiCol_ButtonActive
: return "ButtonActive";
7286 case ImGuiCol_Header
: return "Header";
7287 case ImGuiCol_HeaderHovered
: return "HeaderHovered";
7288 case ImGuiCol_HeaderActive
: return "HeaderActive";
7289 case ImGuiCol_Separator
: return "Separator";
7290 case ImGuiCol_SeparatorHovered
: return "SeparatorHovered";
7291 case ImGuiCol_SeparatorActive
: return "SeparatorActive";
7292 case ImGuiCol_ResizeGrip
: return "ResizeGrip";
7293 case ImGuiCol_ResizeGripHovered
: return "ResizeGripHovered";
7294 case ImGuiCol_ResizeGripActive
: return "ResizeGripActive";
7295 case ImGuiCol_PlotLines
: return "PlotLines";
7296 case ImGuiCol_PlotLinesHovered
: return "PlotLinesHovered";
7297 case ImGuiCol_PlotHistogram
: return "PlotHistogram";
7298 case ImGuiCol_PlotHistogramHovered
: return "PlotHistogramHovered";
7299 case ImGuiCol_TextSelectedBg
: return "TextSelectedBg";
7300 case ImGuiCol_DragDropTarget
: return "DragDropTarget";
7301 case ImGuiCol_NavHighlight
: return "NavHighlight";
7302 case ImGuiCol_NavWindowingHighlight
: return "NavWindowingHighlight";
7303 case ImGuiCol_NavWindowingDimBg
: return "NavWindowingDimBg";
7304 case ImGuiCol_ModalWindowDimBg
: return "ModalWindowDimBg";
7310 bool ImGui::IsWindowChildOf(ImGuiWindow
* window
, ImGuiWindow
* potential_parent
)
7312 if (window
->RootWindow
== potential_parent
)
7314 while (window
!= NULL
)
7316 if (window
== potential_parent
)
7318 window
= window
->ParentWindow
;
7323 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags
)
7325 IM_ASSERT((flags
& ImGuiHoveredFlags_AllowWhenOverlapped
) == 0); // Flags not supported by this function
7326 ImGuiContext
& g
= *GImGui
;
7328 if (flags
& ImGuiHoveredFlags_AnyWindow
)
7330 if (g
.HoveredWindow
== NULL
)
7335 switch (flags
& (ImGuiHoveredFlags_RootWindow
| ImGuiHoveredFlags_ChildWindows
))
7337 case ImGuiHoveredFlags_RootWindow
| ImGuiHoveredFlags_ChildWindows
:
7338 if (g
.HoveredRootWindow
!= g
.CurrentWindow
->RootWindow
)
7341 case ImGuiHoveredFlags_RootWindow
:
7342 if (g
.HoveredWindow
!= g
.CurrentWindow
->RootWindow
)
7345 case ImGuiHoveredFlags_ChildWindows
:
7346 if (g
.HoveredWindow
== NULL
|| !IsWindowChildOf(g
.HoveredWindow
, g
.CurrentWindow
))
7350 if (g
.HoveredWindow
!= g
.CurrentWindow
)
7356 if (!IsWindowContentHoverable(g
.HoveredRootWindow
, flags
))
7358 if (!(flags
& ImGuiHoveredFlags_AllowWhenBlockedByActiveItem
))
7359 if (g
.ActiveId
!= 0 && !g
.ActiveIdAllowOverlap
&& g
.ActiveId
!= g
.HoveredWindow
->MoveId
)
7364 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags
)
7366 ImGuiContext
& g
= *GImGui
;
7367 IM_ASSERT(g
.CurrentWindow
); // Not inside a Begin()/End()
7369 if (flags
& ImGuiFocusedFlags_AnyWindow
)
7370 return g
.NavWindow
!= NULL
;
7372 switch (flags
& (ImGuiFocusedFlags_RootWindow
| ImGuiFocusedFlags_ChildWindows
))
7374 case ImGuiFocusedFlags_RootWindow
| ImGuiFocusedFlags_ChildWindows
:
7375 return g
.NavWindow
&& g
.NavWindow
->RootWindow
== g
.CurrentWindow
->RootWindow
;
7376 case ImGuiFocusedFlags_RootWindow
:
7377 return g
.NavWindow
== g
.CurrentWindow
->RootWindow
;
7378 case ImGuiFocusedFlags_ChildWindows
:
7379 return g
.NavWindow
&& IsWindowChildOf(g
.NavWindow
, g
.CurrentWindow
);
7381 return g
.NavWindow
== g
.CurrentWindow
;
7385 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
7386 bool ImGui::IsWindowNavFocusable(ImGuiWindow
* window
)
7388 ImGuiContext
& g
= *GImGui
;
7389 return window
->Active
&& window
== window
->RootWindow
&& (!(window
->Flags
& ImGuiWindowFlags_NoNavFocus
) || window
== g
.NavWindow
);
7392 float ImGui::GetWindowWidth()
7394 ImGuiWindow
* window
= GImGui
->CurrentWindow
;
7395 return window
->Size
.x
;
7398 float ImGui::GetWindowHeight()
7400 ImGuiWindow
* window
= GImGui
->CurrentWindow
;
7401 return window
->Size
.y
;
7404 ImVec2
ImGui::GetWindowPos()
7406 ImGuiContext
& g
= *GImGui
;
7407 ImGuiWindow
* window
= g
.CurrentWindow
;
7411 static void SetWindowScrollX(ImGuiWindow
* window
, float new_scroll_x
)
7413 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.
7414 window
->Scroll
.x
= new_scroll_x
;
7415 window
->DC
.CursorMaxPos
.x
-= window
->Scroll
.x
;
7418 static void SetWindowScrollY(ImGuiWindow
* window
, float new_scroll_y
)
7420 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.
7421 window
->Scroll
.y
= new_scroll_y
;
7422 window
->DC
.CursorMaxPos
.y
-= window
->Scroll
.y
;
7425 static void SetWindowPos(ImGuiWindow
* window
, const ImVec2
& pos
, ImGuiCond cond
)
7427 // Test condition (NB: bit 0 is always true) and clear flags for next time
7428 if (cond
&& (window
->SetWindowPosAllowFlags
& cond
) == 0)
7431 IM_ASSERT(cond
== 0 || ImIsPowerOfTwo(cond
)); // Make sure the user doesn't attempt to combine multiple condition flags.
7432 window
->SetWindowPosAllowFlags
&= ~(ImGuiCond_Once
| ImGuiCond_FirstUseEver
| ImGuiCond_Appearing
);
7433 window
->SetWindowPosVal
= ImVec2(FLT_MAX
, FLT_MAX
);
7436 const ImVec2 old_pos
= window
->Pos
;
7437 window
->Pos
= ImFloor(pos
);
7438 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
7439 window
->DC
.CursorMaxPos
+= (window
->Pos
- old_pos
); // And more importantly we need to adjust this so size calculation doesn't get affected.
7442 void ImGui::SetWindowPos(const ImVec2
& pos
, ImGuiCond cond
)
7444 ImGuiWindow
* window
= GetCurrentWindowRead();
7445 SetWindowPos(window
, pos
, cond
);
7448 void ImGui::SetWindowPos(const char* name
, const ImVec2
& pos
, ImGuiCond cond
)
7450 if (ImGuiWindow
* window
= FindWindowByName(name
))
7451 SetWindowPos(window
, pos
, cond
);
7454 ImVec2
ImGui::GetWindowSize()
7456 ImGuiWindow
* window
= GetCurrentWindowRead();
7457 return window
->Size
;
7460 static void SetWindowSize(ImGuiWindow
* window
, const ImVec2
& size
, ImGuiCond cond
)
7462 // Test condition (NB: bit 0 is always true) and clear flags for next time
7463 if (cond
&& (window
->SetWindowSizeAllowFlags
& cond
) == 0)
7466 IM_ASSERT(cond
== 0 || ImIsPowerOfTwo(cond
)); // Make sure the user doesn't attempt to combine multiple condition flags.
7467 window
->SetWindowSizeAllowFlags
&= ~(ImGuiCond_Once
| ImGuiCond_FirstUseEver
| ImGuiCond_Appearing
);
7472 window
->AutoFitFramesX
= 0;
7473 window
->SizeFull
.x
= size
.x
;
7477 window
->AutoFitFramesX
= 2;
7478 window
->AutoFitOnlyGrows
= false;
7482 window
->AutoFitFramesY
= 0;
7483 window
->SizeFull
.y
= size
.y
;
7487 window
->AutoFitFramesY
= 2;
7488 window
->AutoFitOnlyGrows
= false;
7492 void ImGui::SetWindowSize(const ImVec2
& size
, ImGuiCond cond
)
7494 SetWindowSize(GImGui
->CurrentWindow
, size
, cond
);
7497 void ImGui::SetWindowSize(const char* name
, const ImVec2
& size
, ImGuiCond cond
)
7499 if (ImGuiWindow
* window
= FindWindowByName(name
))
7500 SetWindowSize(window
, size
, cond
);
7503 static void SetWindowCollapsed(ImGuiWindow
* window
, bool collapsed
, ImGuiCond cond
)
7505 // Test condition (NB: bit 0 is always true) and clear flags for next time
7506 if (cond
&& (window
->SetWindowCollapsedAllowFlags
& cond
) == 0)
7508 window
->SetWindowCollapsedAllowFlags
&= ~(ImGuiCond_Once
| ImGuiCond_FirstUseEver
| ImGuiCond_Appearing
);
7511 window
->Collapsed
= collapsed
;
7514 void ImGui::SetWindowCollapsed(bool collapsed
, ImGuiCond cond
)
7516 SetWindowCollapsed(GImGui
->CurrentWindow
, collapsed
, cond
);
7519 bool ImGui::IsWindowCollapsed()
7521 ImGuiWindow
* window
= GetCurrentWindowRead();
7522 return window
->Collapsed
;
7525 bool ImGui::IsWindowAppearing()
7527 ImGuiWindow
* window
= GetCurrentWindowRead();
7528 return window
->Appearing
;
7531 void ImGui::SetWindowCollapsed(const char* name
, bool collapsed
, ImGuiCond cond
)
7533 if (ImGuiWindow
* window
= FindWindowByName(name
))
7534 SetWindowCollapsed(window
, collapsed
, cond
);
7537 void ImGui::SetWindowFocus()
7539 FocusWindow(GImGui
->CurrentWindow
);
7542 void ImGui::SetWindowFocus(const char* name
)
7546 if (ImGuiWindow
* window
= FindWindowByName(name
))
7547 FocusWindow(window
);
7555 void ImGui::SetNextWindowPos(const ImVec2
& pos
, ImGuiCond cond
, const ImVec2
& pivot
)
7557 ImGuiContext
& g
= *GImGui
;
7558 IM_ASSERT(cond
== 0 || ImIsPowerOfTwo(cond
)); // Make sure the user doesn't attempt to combine multiple condition flags.
7559 g
.NextWindowData
.PosVal
= pos
;
7560 g
.NextWindowData
.PosPivotVal
= pivot
;
7561 g
.NextWindowData
.PosCond
= cond
? cond
: ImGuiCond_Always
;
7564 void ImGui::SetNextWindowSize(const ImVec2
& size
, ImGuiCond cond
)
7566 ImGuiContext
& g
= *GImGui
;
7567 IM_ASSERT(cond
== 0 || ImIsPowerOfTwo(cond
)); // Make sure the user doesn't attempt to combine multiple condition flags.
7568 g
.NextWindowData
.SizeVal
= size
;
7569 g
.NextWindowData
.SizeCond
= cond
? cond
: ImGuiCond_Always
;
7572 void ImGui::SetNextWindowSizeConstraints(const ImVec2
& size_min
, const ImVec2
& size_max
, ImGuiSizeCallback custom_callback
, void* custom_callback_user_data
)
7574 ImGuiContext
& g
= *GImGui
;
7575 g
.NextWindowData
.SizeConstraintCond
= ImGuiCond_Always
;
7576 g
.NextWindowData
.SizeConstraintRect
= ImRect(size_min
, size_max
);
7577 g
.NextWindowData
.SizeCallback
= custom_callback
;
7578 g
.NextWindowData
.SizeCallbackUserData
= custom_callback_user_data
;
7581 void ImGui::SetNextWindowContentSize(const ImVec2
& size
)
7583 ImGuiContext
& g
= *GImGui
;
7584 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.
7585 g
.NextWindowData
.ContentSizeCond
= ImGuiCond_Always
;
7588 void ImGui::SetNextWindowCollapsed(bool collapsed
, ImGuiCond cond
)
7590 ImGuiContext
& g
= *GImGui
;
7591 IM_ASSERT(cond
== 0 || ImIsPowerOfTwo(cond
)); // Make sure the user doesn't attempt to combine multiple condition flags.
7592 g
.NextWindowData
.CollapsedVal
= collapsed
;
7593 g
.NextWindowData
.CollapsedCond
= cond
? cond
: ImGuiCond_Always
;
7596 void ImGui::SetNextWindowFocus()
7598 ImGuiContext
& g
= *GImGui
;
7599 g
.NextWindowData
.FocusCond
= ImGuiCond_Always
; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
7602 void ImGui::SetNextWindowBgAlpha(float alpha
)
7604 ImGuiContext
& g
= *GImGui
;
7605 g
.NextWindowData
.BgAlphaVal
= alpha
;
7606 g
.NextWindowData
.BgAlphaCond
= ImGuiCond_Always
; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
7609 // In window space (not screen space!)
7610 ImVec2
ImGui::GetContentRegionMax()
7612 ImGuiWindow
* window
= GetCurrentWindowRead();
7613 ImVec2 mx
= window
->ContentsRegionRect
.Max
- window
->Pos
;
7614 if (window
->DC
.ColumnsSet
)
7615 mx
.x
= GetColumnOffset(window
->DC
.ColumnsSet
->Current
+ 1) - window
->WindowPadding
.x
;
7619 ImVec2
ImGui::GetContentRegionAvail()
7621 ImGuiWindow
* window
= GetCurrentWindowRead();
7622 return GetContentRegionMax() - (window
->DC
.CursorPos
- window
->Pos
);
7625 float ImGui::GetContentRegionAvailWidth()
7627 return GetContentRegionAvail().x
;
7630 // In window space (not screen space!)
7631 ImVec2
ImGui::GetWindowContentRegionMin()
7633 ImGuiWindow
* window
= GetCurrentWindowRead();
7634 return window
->ContentsRegionRect
.Min
- window
->Pos
;
7637 ImVec2
ImGui::GetWindowContentRegionMax()
7639 ImGuiWindow
* window
= GetCurrentWindowRead();
7640 return window
->ContentsRegionRect
.Max
- window
->Pos
;
7643 float ImGui::GetWindowContentRegionWidth()
7645 ImGuiWindow
* window
= GetCurrentWindowRead();
7646 return window
->ContentsRegionRect
.GetWidth();
7649 float ImGui::GetTextLineHeight()
7651 ImGuiContext
& g
= *GImGui
;
7655 float ImGui::GetTextLineHeightWithSpacing()
7657 ImGuiContext
& g
= *GImGui
;
7658 return g
.FontSize
+ g
.Style
.ItemSpacing
.y
;
7661 float ImGui::GetFrameHeight()
7663 ImGuiContext
& g
= *GImGui
;
7664 return g
.FontSize
+ g
.Style
.FramePadding
.y
* 2.0f
;
7667 float ImGui::GetFrameHeightWithSpacing()
7669 ImGuiContext
& g
= *GImGui
;
7670 return g
.FontSize
+ g
.Style
.FramePadding
.y
* 2.0f
+ g
.Style
.ItemSpacing
.y
;
7673 ImDrawList
* ImGui::GetWindowDrawList()
7675 ImGuiWindow
* window
= GetCurrentWindow();
7676 return window
->DrawList
;
7679 ImFont
* ImGui::GetFont()
7681 return GImGui
->Font
;
7684 float ImGui::GetFontSize()
7686 return GImGui
->FontSize
;
7689 ImVec2
ImGui::GetFontTexUvWhitePixel()
7691 return GImGui
->DrawListSharedData
.TexUvWhitePixel
;
7694 void ImGui::SetWindowFontScale(float scale
)
7696 ImGuiContext
& g
= *GImGui
;
7697 ImGuiWindow
* window
= GetCurrentWindow();
7698 window
->FontWindowScale
= scale
;
7699 g
.FontSize
= g
.DrawListSharedData
.FontSize
= window
->CalcFontSize();
7702 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
7703 // Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'.
7704 ImVec2
ImGui::GetCursorPos()
7706 ImGuiWindow
* window
= GetCurrentWindowRead();
7707 return window
->DC
.CursorPos
- window
->Pos
+ window
->Scroll
;
7710 float ImGui::GetCursorPosX()
7712 ImGuiWindow
* window
= GetCurrentWindowRead();
7713 return window
->DC
.CursorPos
.x
- window
->Pos
.x
+ window
->Scroll
.x
;
7716 float ImGui::GetCursorPosY()
7718 ImGuiWindow
* window
= GetCurrentWindowRead();
7719 return window
->DC
.CursorPos
.y
- window
->Pos
.y
+ window
->Scroll
.y
;
7722 void ImGui::SetCursorPos(const ImVec2
& local_pos
)
7724 ImGuiWindow
* window
= GetCurrentWindow();
7725 window
->DC
.CursorPos
= window
->Pos
- window
->Scroll
+ local_pos
;
7726 window
->DC
.CursorMaxPos
= ImMax(window
->DC
.CursorMaxPos
, window
->DC
.CursorPos
);
7729 void ImGui::SetCursorPosX(float x
)
7731 ImGuiWindow
* window
= GetCurrentWindow();
7732 window
->DC
.CursorPos
.x
= window
->Pos
.x
- window
->Scroll
.x
+ x
;
7733 window
->DC
.CursorMaxPos
.x
= ImMax(window
->DC
.CursorMaxPos
.x
, window
->DC
.CursorPos
.x
);
7736 void ImGui::SetCursorPosY(float y
)
7738 ImGuiWindow
* window
= GetCurrentWindow();
7739 window
->DC
.CursorPos
.y
= window
->Pos
.y
- window
->Scroll
.y
+ y
;
7740 window
->DC
.CursorMaxPos
.y
= ImMax(window
->DC
.CursorMaxPos
.y
, window
->DC
.CursorPos
.y
);
7743 ImVec2
ImGui::GetCursorStartPos()
7745 ImGuiWindow
* window
= GetCurrentWindowRead();
7746 return window
->DC
.CursorStartPos
- window
->Pos
;
7749 ImVec2
ImGui::GetCursorScreenPos()
7751 ImGuiWindow
* window
= GetCurrentWindowRead();
7752 return window
->DC
.CursorPos
;
7755 void ImGui::SetCursorScreenPos(const ImVec2
& screen_pos
)
7757 ImGuiWindow
* window
= GetCurrentWindow();
7758 window
->DC
.CursorPos
= screen_pos
;
7759 window
->DC
.CursorMaxPos
= ImMax(window
->DC
.CursorMaxPos
, window
->DC
.CursorPos
);
7762 float ImGui::GetScrollX()
7764 return GImGui
->CurrentWindow
->Scroll
.x
;
7767 float ImGui::GetScrollY()
7769 return GImGui
->CurrentWindow
->Scroll
.y
;
7772 float ImGui::GetScrollMaxX()
7774 return GetScrollMaxX(GImGui
->CurrentWindow
);
7777 float ImGui::GetScrollMaxY()
7779 return GetScrollMaxY(GImGui
->CurrentWindow
);
7782 void ImGui::SetScrollX(float scroll_x
)
7784 ImGuiWindow
* window
= GetCurrentWindow();
7785 window
->ScrollTarget
.x
= scroll_x
;
7786 window
->ScrollTargetCenterRatio
.x
= 0.0f
;
7789 void ImGui::SetScrollY(float scroll_y
)
7791 ImGuiWindow
* window
= GetCurrentWindow();
7792 window
->ScrollTarget
.y
= scroll_y
+ window
->TitleBarHeight() + window
->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY
7793 window
->ScrollTargetCenterRatio
.y
= 0.0f
;
7796 void ImGui::SetScrollFromPosY(float pos_y
, float center_y_ratio
)
7798 // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
7799 ImGuiWindow
* window
= GetCurrentWindow();
7800 IM_ASSERT(center_y_ratio
>= 0.0f
&& center_y_ratio
<= 1.0f
);
7801 window
->ScrollTarget
.y
= (float)(int)(pos_y
+ window
->Scroll
.y
);
7802 window
->ScrollTargetCenterRatio
.y
= center_y_ratio
;
7805 // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
7806 void ImGui::SetScrollHere(float center_y_ratio
)
7808 ImGuiWindow
* window
= GetCurrentWindow();
7809 float target_y
= window
->DC
.CursorPosPrevLine
.y
- window
->Pos
.y
; // Top of last item, in window space
7810 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.
7811 SetScrollFromPosY(target_y
, center_y_ratio
);
7814 void ImGui::ActivateItem(ImGuiID id
)
7816 ImGuiContext
& g
= *GImGui
;
7817 g
.NavNextActivateId
= id
;
7820 void ImGui::SetKeyboardFocusHere(int offset
)
7822 IM_ASSERT(offset
>= -1); // -1 is allowed but not below
7823 ImGuiWindow
* window
= GetCurrentWindow();
7824 window
->FocusIdxAllRequestNext
= window
->FocusIdxAllCounter
+ 1 + offset
;
7825 window
->FocusIdxTabRequestNext
= INT_MAX
;
7828 void ImGui::SetItemDefaultFocus()
7830 ImGuiContext
& g
= *GImGui
;
7831 ImGuiWindow
* window
= g
.CurrentWindow
;
7832 if (!window
->Appearing
)
7834 if (g
.NavWindow
== window
->RootWindowForNav
&& (g
.NavInitRequest
|| g
.NavInitResultId
!= 0) && g
.NavLayer
== g
.NavWindow
->DC
.NavLayerCurrent
)
7836 g
.NavInitRequest
= false;
7837 g
.NavInitResultId
= g
.NavWindow
->DC
.LastItemId
;
7838 g
.NavInitResultRectRel
= ImRect(g
.NavWindow
->DC
.LastItemRect
.Min
- g
.NavWindow
->Pos
, g
.NavWindow
->DC
.LastItemRect
.Max
- g
.NavWindow
->Pos
);
7839 NavUpdateAnyRequestFlag();
7840 if (!IsItemVisible())
7845 void ImGui::SetStateStorage(ImGuiStorage
* tree
)
7847 ImGuiWindow
* window
= GetCurrentWindow();
7848 window
->DC
.StateStorage
= tree
? tree
: &window
->StateStorage
;
7851 ImGuiStorage
* ImGui::GetStateStorage()
7853 ImGuiWindow
* window
= GetCurrentWindowRead();
7854 return window
->DC
.StateStorage
;
7857 void ImGui::TextV(const char* fmt
, va_list args
)
7859 ImGuiWindow
* window
= GetCurrentWindow();
7860 if (window
->SkipItems
)
7863 ImGuiContext
& g
= *GImGui
;
7864 const char* text_end
= g
.TempBuffer
+ ImFormatStringV(g
.TempBuffer
, IM_ARRAYSIZE(g
.TempBuffer
), fmt
, args
);
7865 TextUnformatted(g
.TempBuffer
, text_end
);
7868 void ImGui::Text(const char* fmt
, ...)
7871 va_start(args
, fmt
);
7876 void ImGui::TextColoredV(const ImVec4
& col
, const char* fmt
, va_list args
)
7878 PushStyleColor(ImGuiCol_Text
, col
);
7883 void ImGui::TextColored(const ImVec4
& col
, const char* fmt
, ...)
7886 va_start(args
, fmt
);
7887 TextColoredV(col
, fmt
, args
);
7891 void ImGui::TextDisabledV(const char* fmt
, va_list args
)
7893 PushStyleColor(ImGuiCol_Text
, GImGui
->Style
.Colors
[ImGuiCol_TextDisabled
]);
7898 void ImGui::TextDisabled(const char* fmt
, ...)
7901 va_start(args
, fmt
);
7902 TextDisabledV(fmt
, args
);
7906 void ImGui::TextWrappedV(const char* fmt
, va_list args
)
7908 bool need_wrap
= (GImGui
->CurrentWindow
->DC
.TextWrapPos
< 0.0f
); // Keep existing wrap position is one ia already set
7909 if (need_wrap
) PushTextWrapPos(0.0f
);
7911 if (need_wrap
) PopTextWrapPos();
7914 void ImGui::TextWrapped(const char* fmt
, ...)
7917 va_start(args
, fmt
);
7918 TextWrappedV(fmt
, args
);
7922 void ImGui::TextUnformatted(const char* text
, const char* text_end
)
7924 ImGuiWindow
* window
= GetCurrentWindow();
7925 if (window
->SkipItems
)
7928 ImGuiContext
& g
= *GImGui
;
7929 IM_ASSERT(text
!= NULL
);
7930 const char* text_begin
= text
;
7931 if (text_end
== NULL
)
7932 text_end
= text
+ strlen(text
); // FIXME-OPT
7934 const ImVec2
text_pos(window
->DC
.CursorPos
.x
, window
->DC
.CursorPos
.y
+ window
->DC
.CurrentLineTextBaseOffset
);
7935 const float wrap_pos_x
= window
->DC
.TextWrapPos
;
7936 const bool wrap_enabled
= wrap_pos_x
>= 0.0f
;
7937 if (text_end
- text
> 2000 && !wrap_enabled
)
7940 // Perform manual coarse clipping to optimize for long multi-line text
7941 // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
7942 // 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.
7943 const char* line
= text
;
7944 const float line_height
= GetTextLineHeight();
7945 const ImRect clip_rect
= window
->ClipRect
;
7946 ImVec2
text_size(0,0);
7948 if (text_pos
.y
<= clip_rect
.Max
.y
)
7950 ImVec2 pos
= text_pos
;
7952 // Lines to skip (can't skip when logging text)
7955 int lines_skippable
= (int)((clip_rect
.Min
.y
- text_pos
.y
) / line_height
);
7956 if (lines_skippable
> 0)
7958 int lines_skipped
= 0;
7959 while (line
< text_end
&& lines_skipped
< lines_skippable
)
7961 const char* line_end
= strchr(line
, '\n');
7963 line_end
= text_end
;
7964 line
= line_end
+ 1;
7967 pos
.y
+= lines_skipped
* line_height
;
7972 if (line
< text_end
)
7974 ImRect
line_rect(pos
, pos
+ ImVec2(FLT_MAX
, line_height
));
7975 while (line
< text_end
)
7977 const char* line_end
= strchr(line
, '\n');
7978 if (IsClippedEx(line_rect
, 0, false))
7981 const ImVec2 line_size
= CalcTextSize(line
, line_end
, false);
7982 text_size
.x
= ImMax(text_size
.x
, line_size
.x
);
7983 RenderText(pos
, line
, line_end
, false);
7985 line_end
= text_end
;
7986 line
= line_end
+ 1;
7987 line_rect
.Min
.y
+= line_height
;
7988 line_rect
.Max
.y
+= line_height
;
7989 pos
.y
+= line_height
;
7992 // Count remaining lines
7993 int lines_skipped
= 0;
7994 while (line
< text_end
)
7996 const char* line_end
= strchr(line
, '\n');
7998 line_end
= text_end
;
7999 line
= line_end
+ 1;
8002 pos
.y
+= lines_skipped
* line_height
;
8005 text_size
.y
+= (pos
- text_pos
).y
;
8008 ImRect
bb(text_pos
, text_pos
+ text_size
);
8014 const float wrap_width
= wrap_enabled
? CalcWrapWidthForPos(window
->DC
.CursorPos
, wrap_pos_x
) : 0.0f
;
8015 const ImVec2 text_size
= CalcTextSize(text_begin
, text_end
, false, wrap_width
);
8017 // Account of baseline offset
8018 ImRect
bb(text_pos
, text_pos
+ text_size
);
8019 ItemSize(text_size
);
8020 if (!ItemAdd(bb
, 0))
8023 // Render (we don't hide text after ## in this end-user function)
8024 RenderTextWrapped(bb
.Min
, text_begin
, text_end
, wrap_width
);
8028 void ImGui::AlignTextToFramePadding()
8030 ImGuiWindow
* window
= GetCurrentWindow();
8031 if (window
->SkipItems
)
8034 ImGuiContext
& g
= *GImGui
;
8035 window
->DC
.CurrentLineHeight
= ImMax(window
->DC
.CurrentLineHeight
, g
.FontSize
+ g
.Style
.FramePadding
.y
* 2);
8036 window
->DC
.CurrentLineTextBaseOffset
= ImMax(window
->DC
.CurrentLineTextBaseOffset
, g
.Style
.FramePadding
.y
);
8039 // Add a label+text combo aligned to other label+value widgets
8040 void ImGui::LabelTextV(const char* label
, const char* fmt
, va_list args
)
8042 ImGuiWindow
* window
= GetCurrentWindow();
8043 if (window
->SkipItems
)
8046 ImGuiContext
& g
= *GImGui
;
8047 const ImGuiStyle
& style
= g
.Style
;
8048 const float w
= CalcItemWidth();
8050 const ImVec2 label_size
= CalcTextSize(label
, NULL
, true);
8051 const ImRect
value_bb(window
->DC
.CursorPos
, window
->DC
.CursorPos
+ ImVec2(w
, label_size
.y
+ style
.FramePadding
.y
*2));
8052 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
);
8053 ItemSize(total_bb
, style
.FramePadding
.y
);
8054 if (!ItemAdd(total_bb
, 0))
8058 const char* value_text_begin
= &g
.TempBuffer
[0];
8059 const char* value_text_end
= value_text_begin
+ ImFormatStringV(g
.TempBuffer
, IM_ARRAYSIZE(g
.TempBuffer
), fmt
, args
);
8060 RenderTextClipped(value_bb
.Min
, value_bb
.Max
, value_text_begin
, value_text_end
, NULL
, ImVec2(0.0f
,0.5f
));
8061 if (label_size
.x
> 0.0f
)
8062 RenderText(ImVec2(value_bb
.Max
.x
+ style
.ItemInnerSpacing
.x
, value_bb
.Min
.y
+ style
.FramePadding
.y
), label
);
8065 void ImGui::LabelText(const char* label
, const char* fmt
, ...)
8068 va_start(args
, fmt
);
8069 LabelTextV(label
, fmt
, args
);
8073 bool ImGui::ButtonBehavior(const ImRect
& bb
, ImGuiID id
, bool* out_hovered
, bool* out_held
, ImGuiButtonFlags flags
)
8075 ImGuiContext
& g
= *GImGui
;
8076 ImGuiWindow
* window
= GetCurrentWindow();
8078 if (flags
& ImGuiButtonFlags_Disabled
)
8080 if (out_hovered
) *out_hovered
= false;
8081 if (out_held
) *out_held
= false;
8082 if (g
.ActiveId
== id
) ClearActiveID();
8086 // Default behavior requires click+release on same spot
8087 if ((flags
& (ImGuiButtonFlags_PressedOnClickRelease
| ImGuiButtonFlags_PressedOnClick
| ImGuiButtonFlags_PressedOnRelease
| ImGuiButtonFlags_PressedOnDoubleClick
)) == 0)
8088 flags
|= ImGuiButtonFlags_PressedOnClickRelease
;
8090 ImGuiWindow
* backup_hovered_window
= g
.HoveredWindow
;
8091 if ((flags
& ImGuiButtonFlags_FlattenChildren
) && g
.HoveredRootWindow
== window
)
8092 g
.HoveredWindow
= window
;
8094 bool pressed
= false;
8095 bool hovered
= ItemHoverable(bb
, id
);
8097 // Drag source doesn't report as hovered
8098 if (hovered
&& g
.DragDropActive
&& g
.DragDropPayload
.SourceId
== id
&& !(g
.DragDropSourceFlags
& ImGuiDragDropFlags_SourceNoDisableHover
))
8101 // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button
8102 if (g
.DragDropActive
&& (flags
& ImGuiButtonFlags_PressedOnDragDropHold
) && !(g
.DragDropSourceFlags
& ImGuiDragDropFlags_SourceNoHoldToOpenOthers
))
8103 if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem
))
8107 if (CalcTypematicPressedRepeatAmount(g
.HoveredIdTimer
+ 0.0001f
, g
.HoveredIdTimer
+ 0.0001f
- g
.IO
.DeltaTime
, 0.01f
, 0.70f
)) // FIXME: Our formula for CalcTypematicPressedRepeatAmount() is fishy
8110 FocusWindow(window
);
8114 if ((flags
& ImGuiButtonFlags_FlattenChildren
) && g
.HoveredRootWindow
== window
)
8115 g
.HoveredWindow
= backup_hovered_window
;
8117 // 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.
8118 if (hovered
&& (flags
& ImGuiButtonFlags_AllowItemOverlap
) && (g
.HoveredIdPreviousFrame
!= id
&& g
.HoveredIdPreviousFrame
!= 0))
8124 if (!(flags
& ImGuiButtonFlags_NoKeyModifiers
) || (!g
.IO
.KeyCtrl
&& !g
.IO
.KeyShift
&& !g
.IO
.KeyAlt
))
8126 // | CLICKING | HOLDING with ImGuiButtonFlags_Repeat
8127 // PressedOnClickRelease | <on release>* | <on repeat> <on repeat> .. (NOT on release) <-- MOST COMMON! (*) only if both click/release were over bounds
8128 // PressedOnClick | <on click> | <on click> <on repeat> <on repeat> ..
8129 // PressedOnRelease | <on release> | <on repeat> <on repeat> .. (NOT on release)
8130 // PressedOnDoubleClick | <on dclick> | <on dclick> <on repeat> <on repeat> ..
8131 // FIXME-NAV: We don't honor those different behaviors.
8132 if ((flags
& ImGuiButtonFlags_PressedOnClickRelease
) && g
.IO
.MouseClicked
[0])
8134 SetActiveID(id
, window
);
8135 if (!(flags
& ImGuiButtonFlags_NoNavFocus
))
8136 SetFocusID(id
, window
);
8137 FocusWindow(window
);
8139 if (((flags
& ImGuiButtonFlags_PressedOnClick
) && g
.IO
.MouseClicked
[0]) || ((flags
& ImGuiButtonFlags_PressedOnDoubleClick
) && g
.IO
.MouseDoubleClicked
[0]))
8142 if (flags
& ImGuiButtonFlags_NoHoldingActiveID
)
8145 SetActiveID(id
, window
); // Hold on ID
8146 FocusWindow(window
);
8148 if ((flags
& ImGuiButtonFlags_PressedOnRelease
) && g
.IO
.MouseReleased
[0])
8150 if (!((flags
& ImGuiButtonFlags_Repeat
) && g
.IO
.MouseDownDurationPrev
[0] >= g
.IO
.KeyRepeatDelay
)) // Repeat mode trumps <on release>
8155 // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above).
8156 // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.
8157 if ((flags
& ImGuiButtonFlags_Repeat
) && g
.ActiveId
== id
&& g
.IO
.MouseDownDuration
[0] > 0.0f
&& IsMouseClicked(0, true))
8162 g
.NavDisableHighlight
= true;
8165 // Gamepad/Keyboard navigation
8166 // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse.
8167 if (g
.NavId
== id
&& !g
.NavDisableHighlight
&& g
.NavDisableMouseHover
&& (g
.ActiveId
== 0 || g
.ActiveId
== id
|| g
.ActiveId
== window
->MoveId
))
8170 if (g
.NavActivateDownId
== id
)
8172 bool nav_activated_by_code
= (g
.NavActivateId
== id
);
8173 bool nav_activated_by_inputs
= IsNavInputPressed(ImGuiNavInput_Activate
, (flags
& ImGuiButtonFlags_Repeat
) ? ImGuiInputReadMode_Repeat
: ImGuiInputReadMode_Pressed
);
8174 if (nav_activated_by_code
|| nav_activated_by_inputs
)
8176 if (nav_activated_by_code
|| nav_activated_by_inputs
|| g
.ActiveId
== id
)
8178 // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button.
8179 g
.NavActivateId
= id
; // This is so SetActiveId assign a Nav source
8180 SetActiveID(id
, window
);
8181 if (!(flags
& ImGuiButtonFlags_NoNavFocus
))
8182 SetFocusID(id
, window
);
8183 g
.ActiveIdAllowNavDirFlags
= (1 << ImGuiDir_Left
) | (1 << ImGuiDir_Right
) | (1 << ImGuiDir_Up
) | (1 << ImGuiDir_Down
);
8188 if (g
.ActiveId
== id
)
8190 if (g
.ActiveIdSource
== ImGuiInputSource_Mouse
)
8192 if (g
.ActiveIdIsJustActivated
)
8193 g
.ActiveIdClickOffset
= g
.IO
.MousePos
- bb
.Min
;
8194 if (g
.IO
.MouseDown
[0])
8200 if (hovered
&& (flags
& ImGuiButtonFlags_PressedOnClickRelease
))
8201 if (!((flags
& ImGuiButtonFlags_Repeat
) && g
.IO
.MouseDownDurationPrev
[0] >= g
.IO
.KeyRepeatDelay
)) // Repeat mode trumps <on release>
8202 if (!g
.DragDropActive
)
8206 if (!(flags
& ImGuiButtonFlags_NoNavFocus
))
8207 g
.NavDisableHighlight
= true;
8209 else if (g
.ActiveIdSource
== ImGuiInputSource_Nav
)
8211 if (g
.NavActivateDownId
!= id
)
8216 if (out_hovered
) *out_hovered
= hovered
;
8217 if (out_held
) *out_held
= held
;
8222 bool ImGui::ButtonEx(const char* label
, const ImVec2
& size_arg
, ImGuiButtonFlags flags
)
8224 ImGuiWindow
* window
= GetCurrentWindow();
8225 if (window
->SkipItems
)
8228 ImGuiContext
& g
= *GImGui
;
8229 const ImGuiStyle
& style
= g
.Style
;
8230 const ImGuiID id
= window
->GetID(label
);
8231 const ImVec2 label_size
= CalcTextSize(label
, NULL
, true);
8233 ImVec2 pos
= window
->DC
.CursorPos
;
8234 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)
8235 pos
.y
+= window
->DC
.CurrentLineTextBaseOffset
- style
.FramePadding
.y
;
8236 ImVec2 size
= CalcItemSize(size_arg
, label_size
.x
+ style
.FramePadding
.x
* 2.0f
, label_size
.y
+ style
.FramePadding
.y
* 2.0f
);
8238 const ImRect
bb(pos
, pos
+ size
);
8239 ItemSize(bb
, style
.FramePadding
.y
);
8240 if (!ItemAdd(bb
, id
))
8243 if (window
->DC
.ItemFlags
& ImGuiItemFlags_ButtonRepeat
)
8244 flags
|= ImGuiButtonFlags_Repeat
;
8246 bool pressed
= ButtonBehavior(bb
, id
, &hovered
, &held
, flags
);
8248 MarkItemValueChanged(id
);
8251 const ImU32 col
= GetColorU32((held
&& hovered
) ? ImGuiCol_ButtonActive
: hovered
? ImGuiCol_ButtonHovered
: ImGuiCol_Button
);
8252 RenderNavHighlight(bb
, id
);
8253 RenderFrame(bb
.Min
, bb
.Max
, col
, true, style
.FrameRounding
);
8254 RenderTextClipped(bb
.Min
+ style
.FramePadding
, bb
.Max
- style
.FramePadding
, label
, NULL
, &label_size
, style
.ButtonTextAlign
, &bb
);
8256 // Automatically close popups
8257 //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
8258 // CloseCurrentPopup();
8263 bool ImGui::Button(const char* label
, const ImVec2
& size_arg
)
8265 return ButtonEx(label
, size_arg
, 0);
8268 // Small buttons fits within text without additional vertical spacing.
8269 bool ImGui::SmallButton(const char* label
)
8271 ImGuiContext
& g
= *GImGui
;
8272 float backup_padding_y
= g
.Style
.FramePadding
.y
;
8273 g
.Style
.FramePadding
.y
= 0.0f
;
8274 bool pressed
= ButtonEx(label
, ImVec2(0,0), ImGuiButtonFlags_AlignTextBaseLine
);
8275 g
.Style
.FramePadding
.y
= backup_padding_y
;
8279 bool ImGui::ArrowButtonEx(const char* str_id
, ImGuiDir dir
, ImVec2 size
, ImGuiButtonFlags flags
)
8281 ImGuiWindow
* window
= GetCurrentWindow();
8282 if (window
->SkipItems
)
8285 ImGuiContext
& g
= *GImGui
;
8286 const ImGuiID id
= window
->GetID(str_id
);
8287 const ImRect
bb(window
->DC
.CursorPos
, window
->DC
.CursorPos
+ size
);
8288 const float default_size
= GetFrameHeight();
8289 ItemSize(bb
, (size
.y
>= default_size
) ? g
.Style
.FramePadding
.y
: 0.0f
);
8290 if (!ItemAdd(bb
, id
))
8293 if (window
->DC
.ItemFlags
& ImGuiItemFlags_ButtonRepeat
)
8294 flags
|= ImGuiButtonFlags_Repeat
;
8297 bool pressed
= ButtonBehavior(bb
, id
, &hovered
, &held
, flags
);
8300 const ImU32 col
= GetColorU32((held
&& hovered
) ? ImGuiCol_ButtonActive
: hovered
? ImGuiCol_ButtonHovered
: ImGuiCol_Button
);
8301 RenderNavHighlight(bb
, id
);
8302 RenderFrame(bb
.Min
, bb
.Max
, col
, true, g
.Style
.FrameRounding
);
8303 RenderArrow(bb
.Min
+ ImVec2(ImMax(0.0f
, size
.x
- g
.FontSize
- g
.Style
.FramePadding
.x
), ImMax(0.0f
, size
.y
- g
.FontSize
- g
.Style
.FramePadding
.y
)), dir
);
8308 bool ImGui::ArrowButton(const char* str_id
, ImGuiDir dir
)
8310 float sz
= GetFrameHeight();
8311 return ArrowButtonEx(str_id
, dir
, ImVec2(sz
, sz
), 0);
8314 // Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack.
8315 // 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)
8316 bool ImGui::InvisibleButton(const char* str_id
, const ImVec2
& size_arg
)
8318 ImGuiWindow
* window
= GetCurrentWindow();
8319 if (window
->SkipItems
)
8322 const ImGuiID id
= window
->GetID(str_id
);
8323 ImVec2 size
= CalcItemSize(size_arg
, 0.0f
, 0.0f
);
8324 const ImRect
bb(window
->DC
.CursorPos
, window
->DC
.CursorPos
+ size
);
8326 if (!ItemAdd(bb
, id
))
8330 bool pressed
= ButtonBehavior(bb
, id
, &hovered
, &held
);
8335 // Button to close a window
8336 bool ImGui::CloseButton(ImGuiID id
, const ImVec2
& pos
, float radius
)
8338 ImGuiContext
& g
= *GImGui
;
8339 ImGuiWindow
* window
= g
.CurrentWindow
;
8341 // We intentionally allow interaction when clipped so that a mechanical Alt,Right,Validate sequence close a window.
8342 // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible).
8343 const ImRect
bb(pos
- ImVec2(radius
,radius
), pos
+ ImVec2(radius
,radius
));
8344 bool is_clipped
= !ItemAdd(bb
, id
);
8347 bool pressed
= ButtonBehavior(bb
, id
, &hovered
, &held
);
8352 ImVec2 center
= bb
.GetCenter();
8354 window
->DrawList
->AddCircleFilled(center
, ImMax(2.0f
, radius
), GetColorU32((held
&& hovered
) ? ImGuiCol_ButtonActive
: ImGuiCol_ButtonHovered
), 9);
8356 float cross_extent
= (radius
* 0.7071f
) - 1.0f
;
8357 ImU32 cross_col
= GetColorU32(ImGuiCol_Text
);
8358 center
-= ImVec2(0.5f
, 0.5f
);
8359 window
->DrawList
->AddLine(center
+ ImVec2(+cross_extent
,+cross_extent
), center
+ ImVec2(-cross_extent
,-cross_extent
), cross_col
, 1.0f
);
8360 window
->DrawList
->AddLine(center
+ ImVec2(+cross_extent
,-cross_extent
), center
+ ImVec2(-cross_extent
,+cross_extent
), cross_col
, 1.0f
);
8365 bool ImGui::CollapseButton(ImGuiID id
, const ImVec2
& pos
)
8367 ImGuiContext
& g
= *GImGui
;
8368 ImGuiWindow
* window
= g
.CurrentWindow
;
8370 ImRect
bb(pos
, pos
+ ImVec2(g
.FontSize
, g
.FontSize
));
8372 bool ret
= ButtonBehavior(bb
, id
, NULL
, NULL
, ImGuiButtonFlags_None
);
8373 RenderNavHighlight(bb
, id
);
8374 RenderArrow(bb
.Min
, window
->Collapsed
? ImGuiDir_Right
: ImGuiDir_Down
, 1.0f
);
8376 // Switch to moving the window after mouse is moved beyond the initial drag threshold
8377 if (IsItemActive() && IsMouseDragging())
8378 StartMouseMovingWindow(window
);
8383 void ImGui::Image(ImTextureID user_texture_id
, const ImVec2
& size
, const ImVec2
& uv0
, const ImVec2
& uv1
, const ImVec4
& tint_col
, const ImVec4
& border_col
)
8385 ImGuiWindow
* window
= GetCurrentWindow();
8386 if (window
->SkipItems
)
8389 ImRect
bb(window
->DC
.CursorPos
, window
->DC
.CursorPos
+ size
);
8390 if (border_col
.w
> 0.0f
)
8391 bb
.Max
+= ImVec2(2,2);
8393 if (!ItemAdd(bb
, 0))
8396 if (border_col
.w
> 0.0f
)
8398 window
->DrawList
->AddRect(bb
.Min
, bb
.Max
, GetColorU32(border_col
), 0.0f
);
8399 window
->DrawList
->AddImage(user_texture_id
, bb
.Min
+ImVec2(1,1), bb
.Max
-ImVec2(1,1), uv0
, uv1
, GetColorU32(tint_col
));
8403 window
->DrawList
->AddImage(user_texture_id
, bb
.Min
, bb
.Max
, uv0
, uv1
, GetColorU32(tint_col
));
8407 // frame_padding < 0: uses FramePadding from style (default)
8408 // frame_padding = 0: no framing
8409 // frame_padding > 0: set framing size
8410 // The color used are the button colors.
8411 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
)
8413 ImGuiWindow
* window
= GetCurrentWindow();
8414 if (window
->SkipItems
)
8417 ImGuiContext
& g
= *GImGui
;
8418 const ImGuiStyle
& style
= g
.Style
;
8420 // Default to using texture ID as ID. User can still push string/integer prefixes.
8421 // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV.
8422 PushID((void *)user_texture_id
);
8423 const ImGuiID id
= window
->GetID("#image");
8426 const ImVec2 padding
= (frame_padding
>= 0) ? ImVec2((float)frame_padding
, (float)frame_padding
) : style
.FramePadding
;
8427 const ImRect
bb(window
->DC
.CursorPos
, window
->DC
.CursorPos
+ size
+ padding
*2);
8428 const ImRect
image_bb(window
->DC
.CursorPos
+ padding
, window
->DC
.CursorPos
+ padding
+ size
);
8430 if (!ItemAdd(bb
, id
))
8434 bool pressed
= ButtonBehavior(bb
, id
, &hovered
, &held
);
8437 const ImU32 col
= GetColorU32((held
&& hovered
) ? ImGuiCol_ButtonActive
: hovered
? ImGuiCol_ButtonHovered
: ImGuiCol_Button
);
8438 RenderNavHighlight(bb
, id
);
8439 RenderFrame(bb
.Min
, bb
.Max
, col
, true, ImClamp((float)ImMin(padding
.x
, padding
.y
), 0.0f
, style
.FrameRounding
));
8440 if (bg_col
.w
> 0.0f
)
8441 window
->DrawList
->AddRectFilled(image_bb
.Min
, image_bb
.Max
, GetColorU32(bg_col
));
8442 window
->DrawList
->AddImage(user_texture_id
, image_bb
.Min
, image_bb
.Max
, uv0
, uv1
, GetColorU32(tint_col
));
8447 // Start logging ImGui output to TTY
8448 void ImGui::LogToTTY(int max_depth
)
8450 ImGuiContext
& g
= *GImGui
;
8453 ImGuiWindow
* window
= g
.CurrentWindow
;
8455 IM_ASSERT(g
.LogFile
== NULL
);
8457 g
.LogEnabled
= true;
8458 g
.LogStartDepth
= window
->DC
.TreeDepth
;
8460 g
.LogAutoExpandMaxDepth
= max_depth
;
8463 // Start logging ImGui output to given file
8464 void ImGui::LogToFile(int max_depth
, const char* filename
)
8466 ImGuiContext
& g
= *GImGui
;
8469 ImGuiWindow
* window
= g
.CurrentWindow
;
8473 filename
= g
.IO
.LogFilename
;
8478 IM_ASSERT(g
.LogFile
== NULL
);
8479 g
.LogFile
= ImFileOpen(filename
, "ab");
8482 IM_ASSERT(g
.LogFile
!= NULL
); // Consider this an error
8485 g
.LogEnabled
= true;
8486 g
.LogStartDepth
= window
->DC
.TreeDepth
;
8488 g
.LogAutoExpandMaxDepth
= max_depth
;
8491 // Start logging ImGui output to clipboard
8492 void ImGui::LogToClipboard(int max_depth
)
8494 ImGuiContext
& g
= *GImGui
;
8497 ImGuiWindow
* window
= g
.CurrentWindow
;
8499 IM_ASSERT(g
.LogFile
== NULL
);
8501 g
.LogEnabled
= true;
8502 g
.LogStartDepth
= window
->DC
.TreeDepth
;
8504 g
.LogAutoExpandMaxDepth
= max_depth
;
8507 void ImGui::LogFinish()
8509 ImGuiContext
& g
= *GImGui
;
8513 LogText(IM_NEWLINE
);
8514 if (g
.LogFile
!= NULL
)
8516 if (g
.LogFile
== stdout
)
8522 if (g
.LogClipboard
.size() > 1)
8524 SetClipboardText(g
.LogClipboard
.begin());
8525 g
.LogClipboard
.clear();
8527 g
.LogEnabled
= false;
8530 // Helper to display logging buttons
8531 void ImGui::LogButtons()
8533 ImGuiContext
& g
= *GImGui
;
8535 PushID("LogButtons");
8536 const bool log_to_tty
= Button("Log To TTY"); SameLine();
8537 const bool log_to_file
= Button("Log To File"); SameLine();
8538 const bool log_to_clipboard
= Button("Log To Clipboard"); SameLine();
8539 PushItemWidth(80.0f
);
8540 PushAllowKeyboardFocus(false);
8541 SliderInt("Depth", &g
.LogAutoExpandMaxDepth
, 0, 9, NULL
);
8542 PopAllowKeyboardFocus();
8546 // Start logging at the end of the function so that the buttons don't appear in the log
8548 LogToTTY(g
.LogAutoExpandMaxDepth
);
8550 LogToFile(g
.LogAutoExpandMaxDepth
, g
.IO
.LogFilename
);
8551 if (log_to_clipboard
)
8552 LogToClipboard(g
.LogAutoExpandMaxDepth
);
8555 bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id
, ImGuiTreeNodeFlags flags
)
8557 if (flags
& ImGuiTreeNodeFlags_Leaf
)
8560 // We only write to the tree storage if the user clicks (or explicitly use SetNextTreeNode*** functions)
8561 ImGuiContext
& g
= *GImGui
;
8562 ImGuiWindow
* window
= g
.CurrentWindow
;
8563 ImGuiStorage
* storage
= window
->DC
.StateStorage
;
8566 if (g
.NextTreeNodeOpenCond
!= 0)
8568 if (g
.NextTreeNodeOpenCond
& ImGuiCond_Always
)
8570 is_open
= g
.NextTreeNodeOpenVal
;
8571 storage
->SetInt(id
, is_open
);
8575 // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently.
8576 const int stored_value
= storage
->GetInt(id
, -1);
8577 if (stored_value
== -1)
8579 is_open
= g
.NextTreeNodeOpenVal
;
8580 storage
->SetInt(id
, is_open
);
8584 is_open
= stored_value
!= 0;
8587 g
.NextTreeNodeOpenCond
= 0;
8591 is_open
= storage
->GetInt(id
, (flags
& ImGuiTreeNodeFlags_DefaultOpen
) ? 1 : 0) != 0;
8594 // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).
8595 // NB- If we are above max depth we still allow manually opened nodes to be logged.
8596 if (g
.LogEnabled
&& !(flags
& ImGuiTreeNodeFlags_NoAutoOpenOnLog
) && window
->DC
.TreeDepth
< g
.LogAutoExpandMaxDepth
)
8602 bool ImGui::TreeNodeBehavior(ImGuiID id
, ImGuiTreeNodeFlags flags
, const char* label
, const char* label_end
)
8604 ImGuiWindow
* window
= GetCurrentWindow();
8605 if (window
->SkipItems
)
8608 ImGuiContext
& g
= *GImGui
;
8609 const ImGuiStyle
& style
= g
.Style
;
8610 const bool display_frame
= (flags
& ImGuiTreeNodeFlags_Framed
) != 0;
8611 const ImVec2 padding
= (display_frame
|| (flags
& ImGuiTreeNodeFlags_FramePadding
)) ? style
.FramePadding
: ImVec2(style
.FramePadding
.x
, 0.0f
);
8614 label_end
= FindRenderedTextEnd(label
);
8615 const ImVec2 label_size
= CalcTextSize(label
, label_end
, false);
8617 // We vertically grow up to current line height up the typical widget height.
8618 const float text_base_offset_y
= ImMax(padding
.y
, window
->DC
.CurrentLineTextBaseOffset
); // Latch before ItemSize changes it
8619 const float frame_height
= ImMax(ImMin(window
->DC
.CurrentLineHeight
, g
.FontSize
+ style
.FramePadding
.y
*2), label_size
.y
+ padding
.y
*2);
8620 ImRect frame_bb
= ImRect(window
->DC
.CursorPos
, ImVec2(window
->Pos
.x
+ GetContentRegionMax().x
, window
->DC
.CursorPos
.y
+ frame_height
));
8623 // Framed header expand a little outside the default padding
8624 frame_bb
.Min
.x
-= (float)(int)(window
->WindowPadding
.x
*0.5f
) - 1;
8625 frame_bb
.Max
.x
+= (float)(int)(window
->WindowPadding
.x
*0.5f
) - 1;
8628 const float text_offset_x
= (g
.FontSize
+ (display_frame
? padding
.x
*3 : padding
.x
*2)); // Collapser arrow width + Spacing
8629 const float text_width
= g
.FontSize
+ (label_size
.x
> 0.0f
? label_size
.x
+ padding
.x
*2 : 0.0f
); // Include collapser
8630 ItemSize(ImVec2(text_width
, frame_height
), text_base_offset_y
);
8632 // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
8633 // (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)
8634 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
);
8635 bool is_open
= TreeNodeBehaviorIsOpen(id
, flags
);
8637 // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child.
8638 // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop().
8639 // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero.
8640 if (is_open
&& !g
.NavIdIsAlive
&& (flags
& ImGuiTreeNodeFlags_NavLeftJumpsBackHere
) && !(flags
& ImGuiTreeNodeFlags_NoTreePushOnOpen
))
8641 window
->DC
.TreeDepthMayJumpToParentOnPop
|= (1 << window
->DC
.TreeDepth
);
8643 bool item_add
= ItemAdd(interact_bb
, id
);
8644 window
->DC
.LastItemStatusFlags
|= ImGuiItemStatusFlags_HasDisplayRect
;
8645 window
->DC
.LastItemDisplayRect
= frame_bb
;
8649 if (is_open
&& !(flags
& ImGuiTreeNodeFlags_NoTreePushOnOpen
))
8654 // Flags that affects opening behavior:
8655 // - 0(default) ..................... single-click anywhere to open
8656 // - OpenOnDoubleClick .............. double-click anywhere to open
8657 // - OpenOnArrow .................... single-click on arrow to open
8658 // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open
8659 ImGuiButtonFlags button_flags
= ImGuiButtonFlags_NoKeyModifiers
| ((flags
& ImGuiTreeNodeFlags_AllowItemOverlap
) ? ImGuiButtonFlags_AllowItemOverlap
: 0);
8660 if (!(flags
& ImGuiTreeNodeFlags_Leaf
))
8661 button_flags
|= ImGuiButtonFlags_PressedOnDragDropHold
;
8662 if (flags
& ImGuiTreeNodeFlags_OpenOnDoubleClick
)
8663 button_flags
|= ImGuiButtonFlags_PressedOnDoubleClick
| ((flags
& ImGuiTreeNodeFlags_OpenOnArrow
) ? ImGuiButtonFlags_PressedOnClickRelease
: 0);
8665 bool hovered
, held
, pressed
= ButtonBehavior(interact_bb
, id
, &hovered
, &held
, button_flags
);
8666 if (!(flags
& ImGuiTreeNodeFlags_Leaf
))
8668 bool toggled
= false;
8671 toggled
= !(flags
& (ImGuiTreeNodeFlags_OpenOnArrow
| ImGuiTreeNodeFlags_OpenOnDoubleClick
)) || (g
.NavActivateId
== id
);
8672 if (flags
& ImGuiTreeNodeFlags_OpenOnArrow
)
8673 toggled
|= IsMouseHoveringRect(interact_bb
.Min
, ImVec2(interact_bb
.Min
.x
+ text_offset_x
, interact_bb
.Max
.y
)) && (!g
.NavDisableMouseHover
);
8674 if (flags
& ImGuiTreeNodeFlags_OpenOnDoubleClick
)
8675 toggled
|= g
.IO
.MouseDoubleClicked
[0];
8676 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.
8680 if (g
.NavId
== id
&& g
.NavMoveRequest
&& g
.NavMoveDir
== ImGuiDir_Left
&& is_open
)
8683 NavMoveRequestCancel();
8685 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?
8688 NavMoveRequestCancel();
8694 window
->DC
.StateStorage
->SetInt(id
, is_open
);
8697 if (flags
& ImGuiTreeNodeFlags_AllowItemOverlap
)
8698 SetItemAllowOverlap();
8701 const ImU32 col
= GetColorU32((held
&& hovered
) ? ImGuiCol_HeaderActive
: hovered
? ImGuiCol_HeaderHovered
: ImGuiCol_Header
);
8702 const ImVec2 text_pos
= frame_bb
.Min
+ ImVec2(text_offset_x
, text_base_offset_y
);
8706 RenderFrame(frame_bb
.Min
, frame_bb
.Max
, col
, true, style
.FrameRounding
);
8707 RenderNavHighlight(frame_bb
, id
, ImGuiNavHighlightFlags_TypeThin
);
8708 RenderArrow(frame_bb
.Min
+ ImVec2(padding
.x
, text_base_offset_y
), is_open
? ImGuiDir_Down
: ImGuiDir_Right
, 1.0f
);
8711 // 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.
8712 const char log_prefix
[] = "\n##";
8713 const char log_suffix
[] = "##";
8714 LogRenderedText(&text_pos
, log_prefix
, log_prefix
+3);
8715 RenderTextClipped(text_pos
, frame_bb
.Max
, label
, label_end
, &label_size
);
8716 LogRenderedText(&text_pos
, log_suffix
+1, log_suffix
+3);
8720 RenderTextClipped(text_pos
, frame_bb
.Max
, label
, label_end
, &label_size
);
8725 // Unframed typed for tree nodes
8726 if (hovered
|| (flags
& ImGuiTreeNodeFlags_Selected
))
8728 RenderFrame(frame_bb
.Min
, frame_bb
.Max
, col
, false);
8729 RenderNavHighlight(frame_bb
, id
, ImGuiNavHighlightFlags_TypeThin
);
8732 if (flags
& ImGuiTreeNodeFlags_Bullet
)
8733 RenderBullet(frame_bb
.Min
+ ImVec2(text_offset_x
* 0.5f
, g
.FontSize
*0.50f
+ text_base_offset_y
));
8734 else if (!(flags
& ImGuiTreeNodeFlags_Leaf
))
8735 RenderArrow(frame_bb
.Min
+ ImVec2(padding
.x
, g
.FontSize
*0.15f
+ text_base_offset_y
), is_open
? ImGuiDir_Down
: ImGuiDir_Right
, 0.70f
);
8737 LogRenderedText(&text_pos
, ">");
8738 RenderText(text_pos
, label
, label_end
, false);
8741 if (is_open
&& !(flags
& ImGuiTreeNodeFlags_NoTreePushOnOpen
))
8746 // CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag).
8747 // This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode().
8748 bool ImGui::CollapsingHeader(const char* label
, ImGuiTreeNodeFlags flags
)
8750 ImGuiWindow
* window
= GetCurrentWindow();
8751 if (window
->SkipItems
)
8754 return TreeNodeBehavior(window
->GetID(label
), flags
| ImGuiTreeNodeFlags_CollapsingHeader
, label
);
8757 bool ImGui::CollapsingHeader(const char* label
, bool* p_open
, ImGuiTreeNodeFlags flags
)
8759 ImGuiWindow
* window
= GetCurrentWindow();
8760 if (window
->SkipItems
)
8763 if (p_open
&& !*p_open
)
8766 ImGuiID id
= window
->GetID(label
);
8767 bool is_open
= TreeNodeBehavior(id
, flags
| ImGuiTreeNodeFlags_CollapsingHeader
| (p_open
? ImGuiTreeNodeFlags_AllowItemOverlap
: 0), label
);
8770 // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
8771 ImGuiContext
& g
= *GImGui
;
8772 float button_sz
= g
.FontSize
* 0.5f
;
8773 ImGuiItemHoveredDataBackup last_item_backup
;
8774 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
))
8776 last_item_backup
.Restore();
8782 bool ImGui::TreeNodeEx(const char* label
, ImGuiTreeNodeFlags flags
)
8784 ImGuiWindow
* window
= GetCurrentWindow();
8785 if (window
->SkipItems
)
8788 return TreeNodeBehavior(window
->GetID(label
), flags
, label
, NULL
);
8791 bool ImGui::TreeNodeExV(const char* str_id
, ImGuiTreeNodeFlags flags
, const char* fmt
, va_list args
)
8793 ImGuiWindow
* window
= GetCurrentWindow();
8794 if (window
->SkipItems
)
8797 ImGuiContext
& g
= *GImGui
;
8798 const char* label_end
= g
.TempBuffer
+ ImFormatStringV(g
.TempBuffer
, IM_ARRAYSIZE(g
.TempBuffer
), fmt
, args
);
8799 return TreeNodeBehavior(window
->GetID(str_id
), flags
, g
.TempBuffer
, label_end
);
8802 bool ImGui::TreeNodeExV(const void* ptr_id
, ImGuiTreeNodeFlags flags
, const char* fmt
, va_list args
)
8804 ImGuiWindow
* window
= GetCurrentWindow();
8805 if (window
->SkipItems
)
8808 ImGuiContext
& g
= *GImGui
;
8809 const char* label_end
= g
.TempBuffer
+ ImFormatStringV(g
.TempBuffer
, IM_ARRAYSIZE(g
.TempBuffer
), fmt
, args
);
8810 return TreeNodeBehavior(window
->GetID(ptr_id
), flags
, g
.TempBuffer
, label_end
);
8813 bool ImGui::TreeNodeV(const char* str_id
, const char* fmt
, va_list args
)
8815 return TreeNodeExV(str_id
, 0, fmt
, args
);
8818 bool ImGui::TreeNodeV(const void* ptr_id
, const char* fmt
, va_list args
)
8820 return TreeNodeExV(ptr_id
, 0, fmt
, args
);
8823 bool ImGui::TreeNodeEx(const char* str_id
, ImGuiTreeNodeFlags flags
, const char* fmt
, ...)
8826 va_start(args
, fmt
);
8827 bool is_open
= TreeNodeExV(str_id
, flags
, fmt
, args
);
8832 bool ImGui::TreeNodeEx(const void* ptr_id
, ImGuiTreeNodeFlags flags
, const char* fmt
, ...)
8835 va_start(args
, fmt
);
8836 bool is_open
= TreeNodeExV(ptr_id
, flags
, fmt
, args
);
8841 bool ImGui::TreeNode(const char* str_id
, const char* fmt
, ...)
8844 va_start(args
, fmt
);
8845 bool is_open
= TreeNodeExV(str_id
, 0, fmt
, args
);
8850 bool ImGui::TreeNode(const void* ptr_id
, const char* fmt
, ...)
8853 va_start(args
, fmt
);
8854 bool is_open
= TreeNodeExV(ptr_id
, 0, fmt
, args
);
8859 bool ImGui::TreeNode(const char* label
)
8861 ImGuiWindow
* window
= GetCurrentWindow();
8862 if (window
->SkipItems
)
8864 return TreeNodeBehavior(window
->GetID(label
), 0, label
, NULL
);
8867 void ImGui::TreeAdvanceToLabelPos()
8869 ImGuiContext
& g
= *GImGui
;
8870 g
.CurrentWindow
->DC
.CursorPos
.x
+= GetTreeNodeToLabelSpacing();
8873 // Horizontal distance preceding label when using TreeNode() or Bullet()
8874 float ImGui::GetTreeNodeToLabelSpacing()
8876 ImGuiContext
& g
= *GImGui
;
8877 return g
.FontSize
+ (g
.Style
.FramePadding
.x
* 2.0f
);
8880 void ImGui::SetNextTreeNodeOpen(bool is_open
, ImGuiCond cond
)
8882 ImGuiContext
& g
= *GImGui
;
8883 if (g
.CurrentWindow
->SkipItems
)
8885 g
.NextTreeNodeOpenVal
= is_open
;
8886 g
.NextTreeNodeOpenCond
= cond
? cond
: ImGuiCond_Always
;
8889 void ImGui::PushID(const char* str_id
)
8891 ImGuiWindow
* window
= GetCurrentWindowRead();
8892 window
->IDStack
.push_back(window
->GetID(str_id
));
8895 void ImGui::PushID(const char* str_id_begin
, const char* str_id_end
)
8897 ImGuiWindow
* window
= GetCurrentWindowRead();
8898 window
->IDStack
.push_back(window
->GetID(str_id_begin
, str_id_end
));
8901 void ImGui::PushID(const void* ptr_id
)
8903 ImGuiWindow
* window
= GetCurrentWindowRead();
8904 window
->IDStack
.push_back(window
->GetID(ptr_id
));
8907 void ImGui::PushID(int int_id
)
8909 const void* ptr_id
= (void*)(intptr_t)int_id
;
8910 ImGuiWindow
* window
= GetCurrentWindowRead();
8911 window
->IDStack
.push_back(window
->GetID(ptr_id
));
8916 ImGuiWindow
* window
= GetCurrentWindowRead();
8917 window
->IDStack
.pop_back();
8920 ImGuiID
ImGui::GetID(const char* str_id
)
8922 return GImGui
->CurrentWindow
->GetID(str_id
);
8925 ImGuiID
ImGui::GetID(const char* str_id_begin
, const char* str_id_end
)
8927 return GImGui
->CurrentWindow
->GetID(str_id_begin
, str_id_end
);
8930 ImGuiID
ImGui::GetID(const void* ptr_id
)
8932 return GImGui
->CurrentWindow
->GetID(ptr_id
);
8935 void ImGui::Bullet()
8937 ImGuiWindow
* window
= GetCurrentWindow();
8938 if (window
->SkipItems
)
8941 ImGuiContext
& g
= *GImGui
;
8942 const ImGuiStyle
& style
= g
.Style
;
8943 const float line_height
= ImMax(ImMin(window
->DC
.CurrentLineHeight
, g
.FontSize
+ g
.Style
.FramePadding
.y
*2), g
.FontSize
);
8944 const ImRect
bb(window
->DC
.CursorPos
, window
->DC
.CursorPos
+ ImVec2(g
.FontSize
, line_height
));
8946 if (!ItemAdd(bb
, 0))
8948 SameLine(0, style
.FramePadding
.x
*2);
8952 // Render and stay on same line
8953 RenderBullet(bb
.Min
+ ImVec2(style
.FramePadding
.x
+ g
.FontSize
*0.5f
, line_height
*0.5f
));
8954 SameLine(0, style
.FramePadding
.x
*2);
8957 // Text with a little bullet aligned to the typical tree node.
8958 void ImGui::BulletTextV(const char* fmt
, va_list args
)
8960 ImGuiWindow
* window
= GetCurrentWindow();
8961 if (window
->SkipItems
)
8964 ImGuiContext
& g
= *GImGui
;
8965 const ImGuiStyle
& style
= g
.Style
;
8967 const char* text_begin
= g
.TempBuffer
;
8968 const char* text_end
= text_begin
+ ImFormatStringV(g
.TempBuffer
, IM_ARRAYSIZE(g
.TempBuffer
), fmt
, args
);
8969 const ImVec2 label_size
= CalcTextSize(text_begin
, text_end
, false);
8970 const float text_base_offset_y
= ImMax(0.0f
, window
->DC
.CurrentLineTextBaseOffset
); // Latch before ItemSize changes it
8971 const float line_height
= ImMax(ImMin(window
->DC
.CurrentLineHeight
, g
.FontSize
+ g
.Style
.FramePadding
.y
*2), g
.FontSize
);
8972 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
8974 if (!ItemAdd(bb
, 0))
8978 RenderBullet(bb
.Min
+ ImVec2(style
.FramePadding
.x
+ g
.FontSize
*0.5f
, line_height
*0.5f
));
8979 RenderText(bb
.Min
+ImVec2(g
.FontSize
+ style
.FramePadding
.x
*2, text_base_offset_y
), text_begin
, text_end
, false);
8982 void ImGui::BulletText(const char* fmt
, ...)
8985 va_start(args
, fmt
);
8986 BulletTextV(fmt
, args
);
8990 static inline int DataTypeFormatString(char* buf
, int buf_size
, ImGuiDataType data_type
, const void* data_ptr
, const char* format
)
8992 if (data_type
== ImGuiDataType_S32
|| data_type
== ImGuiDataType_U32
) // Signedness doesn't matter when pushing the argument
8993 return ImFormatString(buf
, buf_size
, format
, *(const ImU32
*)data_ptr
);
8994 if (data_type
== ImGuiDataType_S64
|| data_type
== ImGuiDataType_U64
) // Signedness doesn't matter when pushing the argument
8995 return ImFormatString(buf
, buf_size
, format
, *(const ImU64
*)data_ptr
);
8996 if (data_type
== ImGuiDataType_Float
)
8997 return ImFormatString(buf
, buf_size
, format
, *(const float*)data_ptr
);
8998 if (data_type
== ImGuiDataType_Double
)
8999 return ImFormatString(buf
, buf_size
, format
, *(const double*)data_ptr
);
9004 // FIXME: Adding support for clamping on boundaries of the data type would be nice.
9005 static void DataTypeApplyOp(ImGuiDataType data_type
, int op
, void* output
, void* arg1
, const void* arg2
)
9007 IM_ASSERT(op
== '+' || op
== '-');
9010 case ImGuiDataType_S32
:
9011 if (op
== '+') *(int*)output
= *(const int*)arg1
+ *(const int*)arg2
;
9012 else if (op
== '-') *(int*)output
= *(const int*)arg1
- *(const int*)arg2
;
9014 case ImGuiDataType_U32
:
9015 if (op
== '+') *(unsigned int*)output
= *(const unsigned int*)arg1
+ *(const ImU32
*)arg2
;
9016 else if (op
== '-') *(unsigned int*)output
= *(const unsigned int*)arg1
- *(const ImU32
*)arg2
;
9018 case ImGuiDataType_S64
:
9019 if (op
== '+') *(ImS64
*)output
= *(const ImS64
*)arg1
+ *(const ImS64
*)arg2
;
9020 else if (op
== '-') *(ImS64
*)output
= *(const ImS64
*)arg1
- *(const ImS64
*)arg2
;
9022 case ImGuiDataType_U64
:
9023 if (op
== '+') *(ImU64
*)output
= *(const ImU64
*)arg1
+ *(const ImU64
*)arg2
;
9024 else if (op
== '-') *(ImU64
*)output
= *(const ImU64
*)arg1
- *(const ImU64
*)arg2
;
9026 case ImGuiDataType_Float
:
9027 if (op
== '+') *(float*)output
= *(const float*)arg1
+ *(const float*)arg2
;
9028 else if (op
== '-') *(float*)output
= *(const float*)arg1
- *(const float*)arg2
;
9030 case ImGuiDataType_Double
:
9031 if (op
== '+') *(double*)output
= *(const double*)arg1
+ *(const double*)arg2
;
9032 else if (op
== '-') *(double*)output
= *(const double*)arg1
- *(const double*)arg2
;
9034 case ImGuiDataType_COUNT
: break;
9039 struct ImGuiDataTypeInfo
9042 const char* PrintFmt
; // Unused
9043 const char* ScanFmt
;
9046 static const ImGuiDataTypeInfo GDataTypeInfo
[] =
9048 { sizeof(int), "%d", "%d" },
9049 { sizeof(unsigned int), "%u", "%u" },
9051 { sizeof(ImS64
), "%I64d","%I64d" },
9052 { sizeof(ImU64
), "%I64u","%I64u" },
9054 { sizeof(ImS64
), "%lld", "%lld" },
9055 { sizeof(ImU64
), "%llu", "%llu" },
9057 { sizeof(float), "%f", "%f" }, // float are promoted to double in va_arg
9058 { sizeof(double), "%f", "%lf" },
9060 IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo
) == ImGuiDataType_COUNT
);
9062 // User can input math operators (e.g. +100) to edit a numerical values.
9063 // NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess..
9064 static bool DataTypeApplyOpFromText(const char* buf
, const char* initial_value_buf
, ImGuiDataType data_type
, void* data_ptr
, const char* format
)
9066 while (ImCharIsBlankA(*buf
))
9069 // We don't support '-' op because it would conflict with inputing negative value.
9070 // Instead you can use +-100 to subtract from an existing value
9072 if (op
== '+' || op
== '*' || op
== '/')
9075 while (ImCharIsBlankA(*buf
))
9085 // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all.
9086 IM_ASSERT(data_type
< ImGuiDataType_COUNT
);
9088 IM_ASSERT(GDataTypeInfo
[data_type
].Size
<= sizeof(data_backup
));
9089 memcpy(data_backup
, data_ptr
, GDataTypeInfo
[data_type
].Size
);
9092 format
= GDataTypeInfo
[data_type
].ScanFmt
;
9095 if (data_type
== ImGuiDataType_S32
)
9097 int* v
= (int*)data_ptr
;
9100 if (op
&& sscanf(initial_value_buf
, format
, &arg0i
) < 1)
9102 // 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
9103 if (op
== '+') { if (sscanf(buf
, "%d", &arg1i
)) *v
= (int)(arg0i
+ arg1i
); } // Add (use "+-" to subtract)
9104 else if (op
== '*') { if (sscanf(buf
, "%f", &arg1f
)) *v
= (int)(arg0i
* arg1f
); } // Multiply
9105 else if (op
== '/') { if (sscanf(buf
, "%f", &arg1f
) && arg1f
!= 0.0f
) *v
= (int)(arg0i
/ arg1f
); } // Divide
9106 else { if (sscanf(buf
, format
, &arg1i
) == 1) *v
= arg1i
; } // Assign constant
9108 else if (data_type
== ImGuiDataType_U32
|| data_type
== ImGuiDataType_S64
|| data_type
== ImGuiDataType_U64
)
9111 // FIXME: We don't bother handling support for legacy operators since they are a little too crappy. Instead we may implement a proper expression evaluator in the future.
9112 sscanf(buf
, format
, data_ptr
);
9114 else if (data_type
== ImGuiDataType_Float
)
9116 // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in
9118 float* v
= (float*)data_ptr
;
9119 float arg0f
= *v
, arg1f
= 0.0f
;
9120 if (op
&& sscanf(initial_value_buf
, format
, &arg0f
) < 1)
9122 if (sscanf(buf
, format
, &arg1f
) < 1)
9124 if (op
== '+') { *v
= arg0f
+ arg1f
; } // Add (use "+-" to subtract)
9125 else if (op
== '*') { *v
= arg0f
* arg1f
; } // Multiply
9126 else if (op
== '/') { if (arg1f
!= 0.0f
) *v
= arg0f
/ arg1f
; } // Divide
9127 else { *v
= arg1f
; } // Assign constant
9129 else if (data_type
== ImGuiDataType_Double
)
9131 format
= "%lf"; // scanf differentiate float/double unlike printf which forces everything to double because of ellipsis
9132 double* v
= (double*)data_ptr
;
9133 double arg0f
= *v
, arg1f
= 0.0;
9134 if (op
&& sscanf(initial_value_buf
, format
, &arg0f
) < 1)
9136 if (sscanf(buf
, format
, &arg1f
) < 1)
9138 if (op
== '+') { *v
= arg0f
+ arg1f
; } // Add (use "+-" to subtract)
9139 else if (op
== '*') { *v
= arg0f
* arg1f
; } // Multiply
9140 else if (op
== '/') { if (arg1f
!= 0.0f
) *v
= arg0f
/ arg1f
; } // Divide
9141 else { *v
= arg1f
; } // Assign constant
9143 return memcmp(data_backup
, data_ptr
, GDataTypeInfo
[data_type
].Size
) != 0;
9146 // Create text input in place of a slider (when CTRL+Clicking on slider)
9147 // FIXME: Logic is messy and confusing.
9148 bool ImGui::InputScalarAsWidgetReplacement(const ImRect
& bb
, ImGuiID id
, const char* label
, ImGuiDataType data_type
, void* data_ptr
, const char* format
)
9150 ImGuiContext
& g
= *GImGui
;
9151 ImGuiWindow
* window
= GetCurrentWindow();
9153 // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen)
9154 // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id
9155 SetActiveID(g
.ScalarAsInputTextId
, window
);
9156 g
.ActiveIdAllowNavDirFlags
= (1 << ImGuiDir_Up
) | (1 << ImGuiDir_Down
);
9158 FocusableItemUnregister(window
);
9162 format
= ImParseFormatTrimDecorations(format
, fmt_buf
, IM_ARRAYSIZE(fmt_buf
));
9163 DataTypeFormatString(data_buf
, IM_ARRAYSIZE(data_buf
), data_type
, data_ptr
, format
);
9164 ImStrTrimBlanks(data_buf
);
9165 ImGuiInputTextFlags flags
= ImGuiInputTextFlags_AutoSelectAll
| ((data_type
== ImGuiDataType_Float
|| data_type
== ImGuiDataType_Double
) ? ImGuiInputTextFlags_CharsScientific
: ImGuiInputTextFlags_CharsDecimal
);
9166 bool value_changed
= InputTextEx(label
, data_buf
, IM_ARRAYSIZE(data_buf
), bb
.GetSize(), flags
);
9167 if (g
.ScalarAsInputTextId
== 0) // First frame we started displaying the InputText widget
9169 IM_ASSERT(g
.ActiveId
== id
); // InputText ID expected to match the Slider ID
9170 g
.ScalarAsInputTextId
= g
.ActiveId
;
9174 return DataTypeApplyOpFromText(data_buf
, g
.InputTextState
.InitialText
.begin(), data_type
, data_ptr
, NULL
);
9178 // We don't use strchr() because our strings are usually very short and often start with '%'
9179 const char* ImParseFormatFindStart(const char* fmt
)
9181 while (char c
= fmt
[0])
9183 if (c
== '%' && fmt
[1] != '%')
9192 const char* ImParseFormatFindEnd(const char* fmt
)
9194 // Printf/scanf types modifiers: I/L/h/j/l/t/w/z. Other uppercase letters qualify as types aka end of the format.
9197 const unsigned int ignored_uppercase_mask
= (1 << ('I'-'A')) | (1 << ('L'-'A'));
9198 const unsigned int ignored_lowercase_mask
= (1 << ('h'-'a')) | (1 << ('j'-'a')) | (1 << ('l'-'a')) | (1 << ('t'-'a')) | (1 << ('w'-'a')) | (1 << ('z'-'a'));
9199 for (char c
; (c
= *fmt
) != 0; fmt
++)
9201 if (c
>= 'A' && c
<= 'Z' && ((1 << (c
- 'A')) & ignored_uppercase_mask
) == 0)
9203 if (c
>= 'a' && c
<= 'z' && ((1 << (c
- 'a')) & ignored_lowercase_mask
) == 0)
9209 // Extract the format out of a format string with leading or trailing decorations
9210 // fmt = "blah blah" -> return fmt
9211 // fmt = "%.3f" -> return fmt
9212 // fmt = "hello %.3f" -> return fmt + 6
9213 // fmt = "%.3f hello" -> return buf written with "%.3f"
9214 const char* ImParseFormatTrimDecorations(const char* fmt
, char* buf
, int buf_size
)
9216 const char* fmt_start
= ImParseFormatFindStart(fmt
);
9217 if (fmt_start
[0] != '%')
9219 const char* fmt_end
= ImParseFormatFindEnd(fmt_start
);
9220 if (fmt_end
[0] == 0) // If we only have leading decoration, we don't need to copy the data.
9222 ImStrncpy(buf
, fmt_start
, ImMin((int)(fmt_end
+ 1 - fmt_start
), buf_size
));
9226 // Parse display precision back from the display format string
9227 // 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.
9228 int ImParseFormatPrecision(const char* fmt
, int default_precision
)
9230 fmt
= ImParseFormatFindStart(fmt
);
9232 return default_precision
;
9234 while (*fmt
>= '0' && *fmt
<= '9')
9236 int precision
= INT_MAX
;
9239 fmt
= ImAtoi
<int>(fmt
+ 1, &precision
);
9240 if (precision
< 0 || precision
> 99)
9241 precision
= default_precision
;
9243 if (*fmt
== 'e' || *fmt
== 'E') // Maximum precision with scientific notation
9245 if ((*fmt
== 'g' || *fmt
== 'G') && precision
== INT_MAX
)
9247 return (precision
== INT_MAX
) ? default_precision
: precision
;
9250 static float GetMinimumStepAtDecimalPrecision(int decimal_precision
)
9252 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
};
9253 if (decimal_precision
< 0)
9255 return (decimal_precision
>= 0 && decimal_precision
< 10) ? min_steps
[decimal_precision
] : ImPow(10.0f
, (float)-decimal_precision
);
9258 template<typename TYPE
, typename SIGNEDTYPE
>
9259 static inline TYPE
RoundScalarWithFormat(const char* format
, ImGuiDataType data_type
, TYPE v
)
9261 const char* fmt_start
= ImParseFormatFindStart(format
);
9262 if (fmt_start
[0] != '%' || fmt_start
[1] == '%') // Don't apply if the value is not visible in the format string
9265 ImFormatString(v_str
, IM_ARRAYSIZE(v_str
), fmt_start
, v
);
9266 const char* p
= v_str
;
9269 if (data_type
== ImGuiDataType_Float
|| data_type
== ImGuiDataType_Double
)
9270 v
= (TYPE
)ImAtof(p
);
9272 ImAtoi(p
, (SIGNEDTYPE
*)&v
);
9276 template<typename TYPE
, typename FLOATTYPE
>
9277 static inline float SliderBehaviorCalcRatioFromValue(ImGuiDataType data_type
, TYPE v
, TYPE v_min
, TYPE v_max
, float power
, float linear_zero_pos
)
9282 const bool is_power
= (power
!= 1.0f
) && (data_type
== ImGuiDataType_Float
|| data_type
== ImGuiDataType_Double
);
9283 const TYPE v_clamped
= (v_min
< v_max
) ? ImClamp(v
, v_min
, v_max
) : ImClamp(v
, v_max
, v_min
);
9286 if (v_clamped
< 0.0f
)
9288 const float f
= 1.0f
- (float)((v_clamped
- v_min
) / (ImMin((TYPE
)0, v_max
) - v_min
));
9289 return (1.0f
- ImPow(f
, 1.0f
/power
)) * linear_zero_pos
;
9293 const float f
= (float)((v_clamped
- ImMax((TYPE
)0, v_min
)) / (v_max
- ImMax((TYPE
)0, v_min
)));
9294 return linear_zero_pos
+ ImPow(f
, 1.0f
/power
) * (1.0f
- linear_zero_pos
);
9299 return (float)((FLOATTYPE
)(v_clamped
- v_min
) / (FLOATTYPE
)(v_max
- v_min
));
9302 // FIXME: Move some of the code into SliderBehavior(). Current responsability is larger than what the equivalent DragBehaviorT<> does, we also do some rendering, etc.
9303 template<typename TYPE
, typename SIGNEDTYPE
, typename FLOATTYPE
>
9304 static bool ImGui::SliderBehaviorT(const ImRect
& bb
, ImGuiID id
, ImGuiDataType data_type
, TYPE
* v
, const TYPE v_min
, const TYPE v_max
, const char* format
, float power
, ImGuiSliderFlags flags
)
9306 ImGuiContext
& g
= *GImGui
;
9307 ImGuiWindow
* window
= g
.CurrentWindow
;
9308 const ImGuiStyle
& style
= g
.Style
;
9310 const bool is_horizontal
= (flags
& ImGuiSliderFlags_Vertical
) == 0;
9311 const bool is_decimal
= (data_type
== ImGuiDataType_Float
) || (data_type
== ImGuiDataType_Double
);
9312 const bool is_power
= (power
!= 1.0f
) && is_decimal
;
9314 const float grab_padding
= 2.0f
;
9315 const float slider_sz
= is_horizontal
? (bb
.GetWidth() - grab_padding
* 2.0f
) : (bb
.GetHeight() - grab_padding
* 2.0f
);
9316 float grab_sz
= style
.GrabMinSize
;
9317 SIGNEDTYPE v_range
= (v_min
< v_max
? v_max
- v_min
: v_min
- v_max
);
9318 if (!is_decimal
&& v_range
>= 0) // v_range < 0 may happen on integer overflows
9319 grab_sz
= ImMax((float)(slider_sz
/ (v_range
+ 1)), style
.GrabMinSize
); // For integer sliders: if possible have the grab size represent 1 unit
9320 grab_sz
= ImMin(grab_sz
, slider_sz
);
9321 const float slider_usable_sz
= slider_sz
- grab_sz
;
9322 const float slider_usable_pos_min
= (is_horizontal
? bb
.Min
.x
: bb
.Min
.y
) + grab_padding
+ grab_sz
*0.5f
;
9323 const float slider_usable_pos_max
= (is_horizontal
? bb
.Max
.x
: bb
.Max
.y
) - grab_padding
- grab_sz
*0.5f
;
9325 // For power curve sliders that cross over sign boundary we want the curve to be symmetric around 0.0f
9326 float linear_zero_pos
; // 0.0->1.0f
9327 if (is_power
&& v_min
* v_max
< 0.0f
)
9330 const FLOATTYPE linear_dist_min_to_0
= ImPow(v_min
>= 0 ? (FLOATTYPE
)v_min
: -(FLOATTYPE
)v_min
, (FLOATTYPE
)1.0f
/power
);
9331 const FLOATTYPE linear_dist_max_to_0
= ImPow(v_max
>= 0 ? (FLOATTYPE
)v_max
: -(FLOATTYPE
)v_max
, (FLOATTYPE
)1.0f
/power
);
9332 linear_zero_pos
= (float)(linear_dist_min_to_0
/ (linear_dist_min_to_0
+ linear_dist_max_to_0
));
9337 linear_zero_pos
= v_min
< 0.0f
? 1.0f
: 0.0f
;
9340 // Process interacting with the slider
9341 bool value_changed
= false;
9342 if (g
.ActiveId
== id
)
9344 bool set_new_value
= false;
9345 float clicked_t
= 0.0f
;
9346 if (g
.ActiveIdSource
== ImGuiInputSource_Mouse
)
9348 if (!g
.IO
.MouseDown
[0])
9354 const float mouse_abs_pos
= is_horizontal
? g
.IO
.MousePos
.x
: g
.IO
.MousePos
.y
;
9355 clicked_t
= (slider_usable_sz
> 0.0f
) ? ImClamp((mouse_abs_pos
- slider_usable_pos_min
) / slider_usable_sz
, 0.0f
, 1.0f
) : 0.0f
;
9357 clicked_t
= 1.0f
- clicked_t
;
9358 set_new_value
= true;
9361 else if (g
.ActiveIdSource
== ImGuiInputSource_Nav
)
9363 const ImVec2 delta2
= GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard
| ImGuiNavDirSourceFlags_PadDPad
, ImGuiInputReadMode_RepeatFast
, 0.0f
, 0.0f
);
9364 float delta
= is_horizontal
? delta2
.x
: -delta2
.y
;
9365 if (g
.NavActivatePressedId
== id
&& !g
.ActiveIdIsJustActivated
)
9369 else if (delta
!= 0.0f
)
9371 clicked_t
= SliderBehaviorCalcRatioFromValue
<TYPE
,FLOATTYPE
>(data_type
, *v
, v_min
, v_max
, power
, linear_zero_pos
);
9372 const int decimal_precision
= is_decimal
? ImParseFormatPrecision(format
, 3) : 0;
9373 if ((decimal_precision
> 0) || is_power
)
9375 delta
/= 100.0f
; // Gamepad/keyboard tweak speeds in % of slider bounds
9376 if (IsNavInputDown(ImGuiNavInput_TweakSlow
))
9381 if ((v_range
>= -100.0f
&& v_range
<= 100.0f
) || IsNavInputDown(ImGuiNavInput_TweakSlow
))
9382 delta
= ((delta
< 0.0f
) ? -1.0f
: +1.0f
) / (float)v_range
; // Gamepad/keyboard tweak speeds in integer steps
9386 if (IsNavInputDown(ImGuiNavInput_TweakFast
))
9388 set_new_value
= true;
9389 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
9390 set_new_value
= false;
9392 clicked_t
= ImSaturate(clicked_t
+ delta
);
9401 // Account for power curve scale on both sides of the zero
9402 if (clicked_t
< linear_zero_pos
)
9404 // Negative: rescale to the negative range before powering
9405 float a
= 1.0f
- (clicked_t
/ linear_zero_pos
);
9406 a
= ImPow(a
, power
);
9407 v_new
= ImLerp(ImMin(v_max
, (TYPE
)0), v_min
, a
);
9411 // Positive: rescale to the positive range before powering
9413 if (ImFabs(linear_zero_pos
- 1.0f
) > 1.e
-6f
)
9414 a
= (clicked_t
- linear_zero_pos
) / (1.0f
- linear_zero_pos
);
9417 a
= ImPow(a
, power
);
9418 v_new
= ImLerp(ImMax(v_min
, (TYPE
)0), v_max
, a
);
9426 v_new
= ImLerp(v_min
, v_max
, clicked_t
);
9430 // For integer values we want the clicking position to match the grab box so we round above
9431 // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property..
9432 FLOATTYPE v_new_off_f
= (v_max
- v_min
) * clicked_t
;
9433 TYPE v_new_off_floor
= (TYPE
)(v_new_off_f
);
9434 TYPE v_new_off_round
= (TYPE
)(v_new_off_f
+ (FLOATTYPE
)0.5);
9435 if (!is_decimal
&& v_new_off_floor
< v_new_off_round
)
9436 v_new
= v_min
+ v_new_off_round
;
9438 v_new
= v_min
+ v_new_off_floor
;
9442 // Round to user desired precision based on format string
9443 v_new
= RoundScalarWithFormat
<TYPE
,SIGNEDTYPE
>(format
, data_type
, v_new
);
9449 value_changed
= true;
9455 float grab_t
= SliderBehaviorCalcRatioFromValue
<TYPE
,FLOATTYPE
>(data_type
, *v
, v_min
, v_max
, power
, linear_zero_pos
);
9457 grab_t
= 1.0f
- grab_t
;
9458 const float grab_pos
= ImLerp(slider_usable_pos_min
, slider_usable_pos_max
, grab_t
);
9461 grab_bb
= ImRect(grab_pos
- grab_sz
*0.5f
, bb
.Min
.y
+ grab_padding
, grab_pos
+ grab_sz
*0.5f
, bb
.Max
.y
- grab_padding
);
9463 grab_bb
= ImRect(bb
.Min
.x
+ grab_padding
, grab_pos
- grab_sz
*0.5f
, bb
.Max
.x
- grab_padding
, grab_pos
+ grab_sz
*0.5f
);
9464 window
->DrawList
->AddRectFilled(grab_bb
.Min
, grab_bb
.Max
, GetColorU32(g
.ActiveId
== id
? ImGuiCol_SliderGrabActive
: ImGuiCol_SliderGrab
), style
.GrabRounding
);
9466 return value_changed
;
9469 // For 32-bits and larger types, slider bounds are limited to half the natural type range.
9470 // So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2 will be ok.
9471 // It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders.
9472 bool ImGui::SliderBehavior(const ImRect
& bb
, ImGuiID id
, ImGuiDataType data_type
, void* v
, const void* v_min
, const void* v_max
, const char* format
, float power
, ImGuiSliderFlags flags
)
9475 ImGuiContext
& g
= *GImGui
;
9476 const ImU32 frame_col
= GetColorU32(g
.ActiveId
== id
? ImGuiCol_FrameBgActive
: g
.HoveredId
== id
? ImGuiCol_FrameBgHovered
: ImGuiCol_FrameBg
);
9477 RenderNavHighlight(bb
, id
);
9478 RenderFrame(bb
.Min
, bb
.Max
, frame_col
, true, g
.Style
.FrameRounding
);
9482 case ImGuiDataType_S32
:
9483 IM_ASSERT(*(const ImS32
*)v_min
>= IM_S32_MIN
/2 && *(const ImS32
*)v_max
<= IM_S32_MAX
/2);
9484 return SliderBehaviorT
<ImS32
, ImS32
, float >(bb
, id
, data_type
, (ImS32
*)v
, *(const ImS32
*)v_min
, *(const ImS32
*)v_max
, format
, power
, flags
);
9485 case ImGuiDataType_U32
:
9486 IM_ASSERT(*(const ImU32
*)v_min
<= IM_U32_MAX
/2);
9487 return SliderBehaviorT
<ImU32
, ImS32
, float >(bb
, id
, data_type
, (ImU32
*)v
, *(const ImU32
*)v_min
, *(const ImU32
*)v_max
, format
, power
, flags
);
9488 case ImGuiDataType_S64
:
9489 IM_ASSERT(*(const ImS64
*)v_min
>= IM_S64_MIN
/2 && *(const ImS64
*)v_max
<= IM_S64_MAX
/2);
9490 return SliderBehaviorT
<ImS64
, ImS64
, double>(bb
, id
, data_type
, (ImS64
*)v
, *(const ImS64
*)v_min
, *(const ImS64
*)v_max
, format
, power
, flags
);
9491 case ImGuiDataType_U64
:
9492 IM_ASSERT(*(const ImU64
*)v_min
<= IM_U64_MAX
/2);
9493 return SliderBehaviorT
<ImU64
, ImS64
, double>(bb
, id
, data_type
, (ImU64
*)v
, *(const ImU64
*)v_min
, *(const ImU64
*)v_max
, format
, power
, flags
);
9494 case ImGuiDataType_Float
:
9495 IM_ASSERT(*(const float*)v_min
>= -FLT_MAX
/2.0f
&& *(const float*)v_max
<= FLT_MAX
/2.0f
);
9496 return SliderBehaviorT
<float, float, float >(bb
, id
, data_type
, (float*)v
, *(const float*)v_min
, *(const float*)v_max
, format
, power
, flags
);
9497 case ImGuiDataType_Double
:
9498 IM_ASSERT(*(const double*)v_min
>= -DBL_MAX
/2.0f
&& *(const double*)v_max
<= DBL_MAX
/2.0f
);
9499 return SliderBehaviorT
<double,double,double>(bb
, id
, data_type
, (double*)v
, *(const double*)v_min
, *(const double*)v_max
, format
, power
, flags
);
9500 case ImGuiDataType_COUNT
: break;
9506 // FIXME-LEGACY: Prior to 1.61 our DragInt() function internally used floats and because of this the compile-time default value for format was "%.0f".
9507 // Even though we changed the compile-time default, we expect users to have carried %f around, which would break the display of DragInt() calls.
9508 // To honor backward compatibility we are rewriting the format string, unless IMGUI_DISABLE_OBSOLETE_FUNCTIONS is enabled. What could possibly go wrong?!
9509 static const char* PatchFormatStringFloatToInt(const char* fmt
)
9511 if (fmt
[0] == '%' && fmt
[1] == '.' && fmt
[2] == '0' && fmt
[3] == 'f' && fmt
[4] == 0) // Fast legacy path for "%.0f" which is expected to be the most common case.
9513 const char* fmt_start
= ImParseFormatFindStart(fmt
); // Find % (if any, and ignore %%)
9514 const char* fmt_end
= ImParseFormatFindEnd(fmt_start
); // Find end of format specifier, which itself is an exercise of confidence/recklessness (because snprintf is dependent on libc or user).
9515 if (fmt_end
> fmt_start
&& fmt_end
[-1] == 'f')
9517 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
9518 if (fmt_start
== fmt
&& fmt_end
[0] == 0)
9520 ImGuiContext
& g
= *GImGui
;
9521 ImFormatString(g
.TempBuffer
, IM_ARRAYSIZE(g
.TempBuffer
), "%.*s%%d%s", (int)(fmt_start
- fmt
), fmt
, fmt_end
); // Honor leading and trailing decorations, but lose alignment/precision.
9522 return g
.TempBuffer
;
9524 IM_ASSERT(0 && "DragInt(): Invalid format string!"); // Old versions used a default parameter of "%.0f", please replace with e.g. "%d"
9530 bool ImGui::SliderScalar(const char* label
, ImGuiDataType data_type
, void* v
, const void* v_min
, const void* v_max
, const char* format
, float power
)
9532 ImGuiWindow
* window
= GetCurrentWindow();
9533 if (window
->SkipItems
)
9536 ImGuiContext
& g
= *GImGui
;
9537 const ImGuiStyle
& style
= g
.Style
;
9538 const ImGuiID id
= window
->GetID(label
);
9539 const float w
= CalcItemWidth();
9541 const ImVec2 label_size
= CalcTextSize(label
, NULL
, true);
9542 const ImRect
frame_bb(window
->DC
.CursorPos
, window
->DC
.CursorPos
+ ImVec2(w
, label_size
.y
+ style
.FramePadding
.y
*2.0f
));
9543 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
));
9545 // NB- we don't call ItemSize() yet because we may turn into a text edit box below
9546 if (!ItemAdd(total_bb
, id
, &frame_bb
))
9548 ItemSize(total_bb
, style
.FramePadding
.y
);
9552 // Default format string when passing NULL
9553 // Patch old "%.0f" format string to use "%d", read function comments for more details.
9554 IM_ASSERT(data_type
>= 0 && data_type
< ImGuiDataType_COUNT
);
9556 format
= GDataTypeInfo
[data_type
].PrintFmt
;
9557 else if (data_type
== ImGuiDataType_S32
&& strcmp(format
, "%d") != 0)
9558 format
= PatchFormatStringFloatToInt(format
);
9560 // Tabbing or CTRL-clicking on Slider turns it into an input box
9561 bool start_text_input
= false;
9562 const bool tab_focus_requested
= FocusableItemRegister(window
, id
);
9563 const bool hovered
= ItemHoverable(frame_bb
, id
);
9564 if (tab_focus_requested
|| (hovered
&& g
.IO
.MouseClicked
[0]) || g
.NavActivateId
== id
|| (g
.NavInputId
== id
&& g
.ScalarAsInputTextId
!= id
))
9566 SetActiveID(id
, window
);
9567 SetFocusID(id
, window
);
9568 FocusWindow(window
);
9569 g
.ActiveIdAllowNavDirFlags
= (1 << ImGuiDir_Up
) | (1 << ImGuiDir_Down
);
9570 if (tab_focus_requested
|| g
.IO
.KeyCtrl
|| g
.NavInputId
== id
)
9572 start_text_input
= true;
9573 g
.ScalarAsInputTextId
= 0;
9576 if (start_text_input
|| (g
.ActiveId
== id
&& g
.ScalarAsInputTextId
== id
))
9577 return InputScalarAsWidgetReplacement(frame_bb
, id
, label
, data_type
, v
, format
);
9579 // Actual slider behavior + render grab
9580 ItemSize(total_bb
, style
.FramePadding
.y
);
9581 const bool value_changed
= SliderBehavior(frame_bb
, id
, data_type
, v
, v_min
, v_max
, format
, power
);
9583 MarkItemValueChanged(id
);
9585 // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
9587 const char* value_buf_end
= value_buf
+ DataTypeFormatString(value_buf
, IM_ARRAYSIZE(value_buf
), data_type
, v
, format
);
9588 RenderTextClipped(frame_bb
.Min
, frame_bb
.Max
, value_buf
, value_buf_end
, NULL
, ImVec2(0.5f
,0.5f
));
9590 if (label_size
.x
> 0.0f
)
9591 RenderText(ImVec2(frame_bb
.Max
.x
+ style
.ItemInnerSpacing
.x
, frame_bb
.Min
.y
+ style
.FramePadding
.y
), label
);
9593 return value_changed
;
9596 bool ImGui::SliderFloat(const char* label
, float* v
, float v_min
, float v_max
, const char* format
, float power
)
9598 return SliderScalar(label
, ImGuiDataType_Float
, v
, &v_min
, &v_max
, format
, power
);
9601 bool ImGui::VSliderScalar(const char* label
, const ImVec2
& size
, ImGuiDataType data_type
, void* v
, const void* v_min
, const void* v_max
, const char* format
, float power
)
9603 ImGuiWindow
* window
= GetCurrentWindow();
9604 if (window
->SkipItems
)
9607 ImGuiContext
& g
= *GImGui
;
9608 const ImGuiStyle
& style
= g
.Style
;
9609 const ImGuiID id
= window
->GetID(label
);
9611 const ImVec2 label_size
= CalcTextSize(label
, NULL
, true);
9612 const ImRect
frame_bb(window
->DC
.CursorPos
, window
->DC
.CursorPos
+ size
);
9613 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
));
9615 ItemSize(bb
, style
.FramePadding
.y
);
9616 if (!ItemAdd(frame_bb
, id
))
9619 // Default format string when passing NULL
9620 // Patch old "%.0f" format string to use "%d", read function comments for more details.
9621 IM_ASSERT(data_type
>= 0 && data_type
< ImGuiDataType_COUNT
);
9623 format
= GDataTypeInfo
[data_type
].PrintFmt
;
9624 else if (data_type
== ImGuiDataType_S32
&& strcmp(format
, "%d") != 0)
9625 format
= PatchFormatStringFloatToInt(format
);
9627 const bool hovered
= ItemHoverable(frame_bb
, id
);
9628 if ((hovered
&& g
.IO
.MouseClicked
[0]) || g
.NavActivateId
== id
|| g
.NavInputId
== id
)
9630 SetActiveID(id
, window
);
9631 SetFocusID(id
, window
);
9632 FocusWindow(window
);
9633 g
.ActiveIdAllowNavDirFlags
= (1 << ImGuiDir_Left
) | (1 << ImGuiDir_Right
);
9636 // Actual slider behavior + render grab
9637 const bool value_changed
= SliderBehavior(frame_bb
, id
, data_type
, v
, v_min
, v_max
, format
, power
, ImGuiSliderFlags_Vertical
);
9639 MarkItemValueChanged(id
);
9641 // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
9642 // For the vertical slider we allow centered text to overlap the frame padding
9644 const char* value_buf_end
= value_buf
+ DataTypeFormatString(value_buf
, IM_ARRAYSIZE(value_buf
), data_type
, v
, format
);
9645 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
));
9646 if (label_size
.x
> 0.0f
)
9647 RenderText(ImVec2(frame_bb
.Max
.x
+ style
.ItemInnerSpacing
.x
, frame_bb
.Min
.y
+ style
.FramePadding
.y
), label
);
9649 return value_changed
;
9652 bool ImGui::SliderAngle(const char* label
, float* v_rad
, float v_degrees_min
, float v_degrees_max
)
9654 float v_deg
= (*v_rad
) * 360.0f
/ (2*IM_PI
);
9655 bool value_changed
= SliderFloat(label
, &v_deg
, v_degrees_min
, v_degrees_max
, "%.0f deg", 1.0f
);
9656 *v_rad
= v_deg
* (2*IM_PI
) / 360.0f
;
9657 return value_changed
;
9660 bool ImGui::SliderInt(const char* label
, int* v
, int v_min
, int v_max
, const char* format
)
9662 return SliderScalar(label
, ImGuiDataType_S32
, v
, &v_min
, &v_max
, format
);
9665 bool ImGui::VSliderFloat(const char* label
, const ImVec2
& size
, float* v
, float v_min
, float v_max
, const char* format
, float power
)
9667 return VSliderScalar(label
, size
, ImGuiDataType_Float
, v
, &v_min
, &v_max
, format
, power
);
9670 bool ImGui::VSliderInt(const char* label
, const ImVec2
& size
, int* v
, int v_min
, int v_max
, const char* format
)
9672 return VSliderScalar(label
, size
, ImGuiDataType_S32
, v
, &v_min
, &v_max
, format
);
9675 // Add multiple sliders on 1 line for compact edition of multiple components
9676 bool ImGui::SliderScalarN(const char* label
, ImGuiDataType data_type
, void* v
, int components
, const void* v_min
, const void* v_max
, const char* format
, float power
)
9678 ImGuiWindow
* window
= GetCurrentWindow();
9679 if (window
->SkipItems
)
9682 ImGuiContext
& g
= *GImGui
;
9683 bool value_changed
= false;
9686 PushMultiItemsWidths(components
);
9687 size_t type_size
= GDataTypeInfo
[data_type
].Size
;
9688 for (int i
= 0; i
< components
; i
++)
9691 value_changed
|= SliderScalar("##v", data_type
, v
, v_min
, v_max
, format
, power
);
9692 SameLine(0, g
.Style
.ItemInnerSpacing
.x
);
9695 v
= (void*)((char*)v
+ type_size
);
9699 TextUnformatted(label
, FindRenderedTextEnd(label
));
9701 return value_changed
;
9704 bool ImGui::SliderFloat2(const char* label
, float v
[2], float v_min
, float v_max
, const char* format
, float power
)
9706 return SliderScalarN(label
, ImGuiDataType_Float
, v
, 2, &v_min
, &v_max
, format
, power
);
9709 bool ImGui::SliderFloat3(const char* label
, float v
[3], float v_min
, float v_max
, const char* format
, float power
)
9711 return SliderScalarN(label
, ImGuiDataType_Float
, v
, 3, &v_min
, &v_max
, format
, power
);
9714 bool ImGui::SliderFloat4(const char* label
, float v
[4], float v_min
, float v_max
, const char* format
, float power
)
9716 return SliderScalarN(label
, ImGuiDataType_Float
, v
, 4, &v_min
, &v_max
, format
, power
);
9719 bool ImGui::SliderInt2(const char* label
, int v
[2], int v_min
, int v_max
, const char* format
)
9721 return SliderScalarN(label
, ImGuiDataType_S32
, v
, 2, &v_min
, &v_max
, format
);
9724 bool ImGui::SliderInt3(const char* label
, int v
[3], int v_min
, int v_max
, const char* format
)
9726 return SliderScalarN(label
, ImGuiDataType_S32
, v
, 3, &v_min
, &v_max
, format
);
9729 bool ImGui::SliderInt4(const char* label
, int v
[4], int v_min
, int v_max
, const char* format
)
9731 return SliderScalarN(label
, ImGuiDataType_S32
, v
, 4, &v_min
, &v_max
, format
);
9734 // This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls)
9735 template<typename TYPE
, typename SIGNEDTYPE
, typename FLOATTYPE
>
9736 static bool ImGui::DragBehaviorT(ImGuiDataType data_type
, TYPE
* v
, float v_speed
, const TYPE v_min
, const TYPE v_max
, const char* format
, float power
)
9738 ImGuiContext
& g
= *GImGui
;
9740 // Default tweak speed
9741 bool has_min_max
= (v_min
!= v_max
) && (v_max
- v_max
< FLT_MAX
);
9742 if (v_speed
== 0.0f
&& has_min_max
)
9743 v_speed
= (float)((v_max
- v_min
) * g
.DragSpeedDefaultRatio
);
9745 // Inputs accumulates into g.DragCurrentAccum, which is flushed into the current value as soon as it makes a difference with our precision settings
9746 float adjust_delta
= 0.0f
;
9747 if (g
.ActiveIdSource
== ImGuiInputSource_Mouse
&& IsMousePosValid() && g
.IO
.MouseDragMaxDistanceSqr
[0] > 1.0f
*1.0f
)
9749 adjust_delta
= g
.IO
.MouseDelta
.x
;
9751 adjust_delta
*= 1.0f
/100.0f
;
9753 adjust_delta
*= 10.0f
;
9755 else if (g
.ActiveIdSource
== ImGuiInputSource_Nav
)
9757 int decimal_precision
= (data_type
== ImGuiDataType_Float
|| data_type
== ImGuiDataType_Double
) ? ImParseFormatPrecision(format
, 3) : 0;
9758 adjust_delta
= GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard
|ImGuiNavDirSourceFlags_PadDPad
, ImGuiInputReadMode_RepeatFast
, 1.0f
/10.0f
, 10.0f
).x
;
9759 v_speed
= ImMax(v_speed
, GetMinimumStepAtDecimalPrecision(decimal_precision
));
9761 adjust_delta
*= v_speed
;
9763 // Clear current value on activation
9764 // Avoid altering values and clamping 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.
9765 bool is_just_activated
= g
.ActiveIdIsJustActivated
;
9766 bool is_already_past_limits_and_pushing_outward
= has_min_max
&& ((*v
>= v_max
&& adjust_delta
> 0.0f
) || (*v
<= v_min
&& adjust_delta
< 0.0f
));
9767 if (is_just_activated
|| is_already_past_limits_and_pushing_outward
)
9769 g
.DragCurrentAccum
= 0.0f
;
9770 g
.DragCurrentAccumDirty
= false;
9772 else if (adjust_delta
!= 0.0f
)
9774 g
.DragCurrentAccum
+= adjust_delta
;
9775 g
.DragCurrentAccumDirty
= true;
9778 if (!g
.DragCurrentAccumDirty
)
9782 FLOATTYPE v_old_ref_for_accum_remainder
= (FLOATTYPE
)0.0f
;
9784 const bool is_power
= (power
!= 1.0f
&& (data_type
== ImGuiDataType_Float
|| data_type
== ImGuiDataType_Double
) && has_min_max
);
9787 // Offset + round to user desired precision, with a curve on the v_min..v_max range to get more precision on one side of the range
9788 FLOATTYPE v_old_norm_curved
= ImPow((FLOATTYPE
)(v_cur
- v_min
) / (FLOATTYPE
)(v_max
- v_min
), (FLOATTYPE
)1.0f
/ power
);
9789 FLOATTYPE v_new_norm_curved
= v_old_norm_curved
+ (g
.DragCurrentAccum
/ (v_max
- v_min
));
9790 v_cur
= v_min
+ (TYPE
)ImPow(ImSaturate((float)v_new_norm_curved
), power
) * (v_max
- v_min
);
9791 v_old_ref_for_accum_remainder
= v_old_norm_curved
;
9795 v_cur
+= (TYPE
)g
.DragCurrentAccum
;
9798 // Round to user desired precision based on format string
9799 v_cur
= RoundScalarWithFormat
<TYPE
, SIGNEDTYPE
>(format
, data_type
, v_cur
);
9801 // Preserve remainder after rounding has been applied. This also allow slow tweaking of values.
9802 g
.DragCurrentAccumDirty
= false;
9805 FLOATTYPE v_cur_norm_curved
= ImPow((FLOATTYPE
)(v_cur
- v_min
) / (FLOATTYPE
)(v_max
- v_min
), (FLOATTYPE
)1.0f
/ power
);
9806 g
.DragCurrentAccum
-= (float)(v_cur_norm_curved
- v_old_ref_for_accum_remainder
);
9810 g
.DragCurrentAccum
-= (float)((SIGNEDTYPE
)v_cur
- (SIGNEDTYPE
)*v
);
9813 // Lose zero sign for float/double
9814 if (v_cur
== (TYPE
)-0)
9817 // Clamp values (handle overflow/wrap-around)
9818 if (*v
!= v_cur
&& has_min_max
)
9820 if (v_cur
< v_min
|| (v_cur
> *v
&& adjust_delta
< 0.0f
))
9822 if (v_cur
> v_max
|| (v_cur
< *v
&& adjust_delta
> 0.0f
))
9833 bool ImGui::DragBehavior(ImGuiID id
, ImGuiDataType data_type
, void* v
, float v_speed
, const void* v_min
, const void* v_max
, const char* format
, float power
)
9835 ImGuiContext
& g
= *GImGui
;
9836 if (g
.ActiveId
== id
)
9838 if (g
.ActiveIdSource
== ImGuiInputSource_Mouse
&& !g
.IO
.MouseDown
[0])
9840 else if (g
.ActiveIdSource
== ImGuiInputSource_Nav
&& g
.NavActivatePressedId
== id
&& !g
.ActiveIdIsJustActivated
)
9843 if (g
.ActiveId
!= id
)
9848 case ImGuiDataType_S32
: return DragBehaviorT
<ImS32
, ImS32
, float >(data_type
, (ImS32
*)v
, v_speed
, v_min
? *(const ImS32
* )v_min
: IM_S32_MIN
, v_max
? *(const ImS32
* )v_max
: IM_S32_MAX
, format
, power
);
9849 case ImGuiDataType_U32
: return DragBehaviorT
<ImU32
, ImS32
, float >(data_type
, (ImU32
*)v
, v_speed
, v_min
? *(const ImU32
* )v_min
: IM_U32_MIN
, v_max
? *(const ImU32
* )v_max
: IM_U32_MAX
, format
, power
);
9850 case ImGuiDataType_S64
: return DragBehaviorT
<ImS64
, ImS64
, double>(data_type
, (ImS64
*)v
, v_speed
, v_min
? *(const ImS64
* )v_min
: IM_S64_MIN
, v_max
? *(const ImS64
* )v_max
: IM_S64_MAX
, format
, power
);
9851 case ImGuiDataType_U64
: return DragBehaviorT
<ImU64
, ImS64
, double>(data_type
, (ImU64
*)v
, v_speed
, v_min
? *(const ImU64
* )v_min
: IM_U64_MIN
, v_max
? *(const ImU64
* )v_max
: IM_U64_MAX
, format
, power
);
9852 case ImGuiDataType_Float
: return DragBehaviorT
<float, float, float >(data_type
, (float*)v
, v_speed
, v_min
? *(const float* )v_min
: -FLT_MAX
, v_max
? *(const float* )v_max
: FLT_MAX
, format
, power
);
9853 case ImGuiDataType_Double
: return DragBehaviorT
<double,double,double>(data_type
, (double*)v
, v_speed
, v_min
? *(const double*)v_min
: -DBL_MAX
, v_max
? *(const double*)v_max
: DBL_MAX
, format
, power
);
9854 case ImGuiDataType_COUNT
: break;
9860 bool ImGui::DragScalar(const char* label
, ImGuiDataType data_type
, void* v
, float v_speed
, const void* v_min
, const void* v_max
, const char* format
, float power
)
9862 ImGuiWindow
* window
= GetCurrentWindow();
9863 if (window
->SkipItems
)
9867 IM_ASSERT(v_min
!= NULL
&& v_max
!= NULL
); // When using a power curve the drag needs to have known bounds
9869 ImGuiContext
& g
= *GImGui
;
9870 const ImGuiStyle
& style
= g
.Style
;
9871 const ImGuiID id
= window
->GetID(label
);
9872 const float w
= CalcItemWidth();
9874 const ImVec2 label_size
= CalcTextSize(label
, NULL
, true);
9875 const ImRect
frame_bb(window
->DC
.CursorPos
, window
->DC
.CursorPos
+ ImVec2(w
, label_size
.y
+ style
.FramePadding
.y
*2.0f
));
9876 const ImRect
inner_bb(frame_bb
.Min
+ style
.FramePadding
, frame_bb
.Max
- style
.FramePadding
);
9877 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
));
9879 // NB- we don't call ItemSize() yet because we may turn into a text edit box below
9880 if (!ItemAdd(total_bb
, id
, &frame_bb
))
9882 ItemSize(total_bb
, style
.FramePadding
.y
);
9885 const bool hovered
= ItemHoverable(frame_bb
, id
);
9887 // Default format string when passing NULL
9888 // Patch old "%.0f" format string to use "%d", read function comments for more details.
9889 IM_ASSERT(data_type
>= 0 && data_type
< ImGuiDataType_COUNT
);
9891 format
= GDataTypeInfo
[data_type
].PrintFmt
;
9892 else if (data_type
== ImGuiDataType_S32
&& strcmp(format
, "%d") != 0)
9893 format
= PatchFormatStringFloatToInt(format
);
9895 // Tabbing or CTRL-clicking on Drag turns it into an input box
9896 bool start_text_input
= false;
9897 const bool tab_focus_requested
= FocusableItemRegister(window
, id
);
9898 if (tab_focus_requested
|| (hovered
&& (g
.IO
.MouseClicked
[0] || g
.IO
.MouseDoubleClicked
[0])) || g
.NavActivateId
== id
|| (g
.NavInputId
== id
&& g
.ScalarAsInputTextId
!= id
))
9900 SetActiveID(id
, window
);
9901 SetFocusID(id
, window
);
9902 FocusWindow(window
);
9903 g
.ActiveIdAllowNavDirFlags
= (1 << ImGuiDir_Up
) | (1 << ImGuiDir_Down
);
9904 if (tab_focus_requested
|| g
.IO
.KeyCtrl
|| g
.IO
.MouseDoubleClicked
[0] || g
.NavInputId
== id
)
9906 start_text_input
= true;
9907 g
.ScalarAsInputTextId
= 0;
9910 if (start_text_input
|| (g
.ActiveId
== id
&& g
.ScalarAsInputTextId
== id
))
9911 return InputScalarAsWidgetReplacement(frame_bb
, id
, label
, data_type
, v
, format
);
9913 // Actual drag behavior
9914 ItemSize(total_bb
, style
.FramePadding
.y
);
9915 const bool value_changed
= DragBehavior(id
, data_type
, v
, v_speed
, v_min
, v_max
, format
, power
);
9917 MarkItemValueChanged(id
);
9920 const ImU32 frame_col
= GetColorU32(g
.ActiveId
== id
? ImGuiCol_FrameBgActive
: g
.HoveredId
== id
? ImGuiCol_FrameBgHovered
: ImGuiCol_FrameBg
);
9921 RenderNavHighlight(frame_bb
, id
);
9922 RenderFrame(frame_bb
.Min
, frame_bb
.Max
, frame_col
, true, style
.FrameRounding
);
9924 // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
9926 const char* value_buf_end
= value_buf
+ DataTypeFormatString(value_buf
, IM_ARRAYSIZE(value_buf
), data_type
, v
, format
);
9927 RenderTextClipped(frame_bb
.Min
, frame_bb
.Max
, value_buf
, value_buf_end
, NULL
, ImVec2(0.5f
, 0.5f
));
9929 if (label_size
.x
> 0.0f
)
9930 RenderText(ImVec2(frame_bb
.Max
.x
+ style
.ItemInnerSpacing
.x
, inner_bb
.Min
.y
), label
);
9932 return value_changed
;
9935 bool ImGui::DragScalarN(const char* label
, ImGuiDataType data_type
, void* v
, int components
, float v_speed
, const void* v_min
, const void* v_max
, const char* format
, float power
)
9937 ImGuiWindow
* window
= GetCurrentWindow();
9938 if (window
->SkipItems
)
9941 ImGuiContext
& g
= *GImGui
;
9942 bool value_changed
= false;
9945 PushMultiItemsWidths(components
);
9946 size_t type_size
= GDataTypeInfo
[data_type
].Size
;
9947 for (int i
= 0; i
< components
; i
++)
9950 value_changed
|= DragScalar("##v", data_type
, v
, v_speed
, v_min
, v_max
, format
, power
);
9951 SameLine(0, g
.Style
.ItemInnerSpacing
.x
);
9954 v
= (void*)((char*)v
+ type_size
);
9958 TextUnformatted(label
, FindRenderedTextEnd(label
));
9960 return value_changed
;
9963 bool ImGui::DragFloat(const char* label
, float* v
, float v_speed
, float v_min
, float v_max
, const char* format
, float power
)
9965 return DragScalar(label
, ImGuiDataType_Float
, v
, v_speed
, &v_min
, &v_max
, format
, power
);
9968 bool ImGui::DragFloat2(const char* label
, float v
[2], float v_speed
, float v_min
, float v_max
, const char* format
, float power
)
9970 return DragScalarN(label
, ImGuiDataType_Float
, v
, 2, v_speed
, &v_min
, &v_max
, format
, power
);
9973 bool ImGui::DragFloat3(const char* label
, float v
[3], float v_speed
, float v_min
, float v_max
, const char* format
, float power
)
9975 return DragScalarN(label
, ImGuiDataType_Float
, v
, 3, v_speed
, &v_min
, &v_max
, format
, power
);
9978 bool ImGui::DragFloat4(const char* label
, float v
[4], float v_speed
, float v_min
, float v_max
, const char* format
, float power
)
9980 return DragScalarN(label
, ImGuiDataType_Float
, v
, 4, v_speed
, &v_min
, &v_max
, format
, power
);
9983 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
)
9985 ImGuiWindow
* window
= GetCurrentWindow();
9986 if (window
->SkipItems
)
9989 ImGuiContext
& g
= *GImGui
;
9992 PushMultiItemsWidths(2);
9994 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
);
9996 SameLine(0, g
.Style
.ItemInnerSpacing
.x
);
9997 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
);
9999 SameLine(0, g
.Style
.ItemInnerSpacing
.x
);
10001 TextUnformatted(label
, FindRenderedTextEnd(label
));
10004 return value_changed
;
10007 // NB: v_speed is float to allow adjusting the drag speed with more precision
10008 bool ImGui::DragInt(const char* label
, int* v
, float v_speed
, int v_min
, int v_max
, const char* format
)
10010 return DragScalar(label
, ImGuiDataType_S32
, v
, v_speed
, &v_min
, &v_max
, format
);
10013 bool ImGui::DragInt2(const char* label
, int v
[2], float v_speed
, int v_min
, int v_max
, const char* format
)
10015 return DragScalarN(label
, ImGuiDataType_S32
, v
, 2, v_speed
, &v_min
, &v_max
, format
);
10018 bool ImGui::DragInt3(const char* label
, int v
[3], float v_speed
, int v_min
, int v_max
, const char* format
)
10020 return DragScalarN(label
, ImGuiDataType_S32
, v
, 3, v_speed
, &v_min
, &v_max
, format
);
10023 bool ImGui::DragInt4(const char* label
, int v
[4], float v_speed
, int v_min
, int v_max
, const char* format
)
10025 return DragScalarN(label
, ImGuiDataType_S32
, v
, 4, v_speed
, &v_min
, &v_max
, format
);
10028 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
)
10030 ImGuiWindow
* window
= GetCurrentWindow();
10031 if (window
->SkipItems
)
10034 ImGuiContext
& g
= *GImGui
;
10037 PushMultiItemsWidths(2);
10039 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
);
10041 SameLine(0, g
.Style
.ItemInnerSpacing
.x
);
10042 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
);
10044 SameLine(0, g
.Style
.ItemInnerSpacing
.x
);
10046 TextUnformatted(label
, FindRenderedTextEnd(label
));
10050 return value_changed
;
10053 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
)
10055 ImGuiWindow
* window
= GetCurrentWindow();
10056 if (window
->SkipItems
)
10059 ImGuiContext
& g
= *GImGui
;
10060 const ImGuiStyle
& style
= g
.Style
;
10062 const ImVec2 label_size
= CalcTextSize(label
, NULL
, true);
10063 if (graph_size
.x
== 0.0f
)
10064 graph_size
.x
= CalcItemWidth();
10065 if (graph_size
.y
== 0.0f
)
10066 graph_size
.y
= label_size
.y
+ (style
.FramePadding
.y
* 2);
10068 const ImRect
frame_bb(window
->DC
.CursorPos
, window
->DC
.CursorPos
+ ImVec2(graph_size
.x
, graph_size
.y
));
10069 const ImRect
inner_bb(frame_bb
.Min
+ style
.FramePadding
, frame_bb
.Max
- style
.FramePadding
);
10070 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));
10071 ItemSize(total_bb
, style
.FramePadding
.y
);
10072 if (!ItemAdd(total_bb
, 0, &frame_bb
))
10074 const bool hovered
= ItemHoverable(inner_bb
, 0);
10076 // Determine scale from values if not specified
10077 if (scale_min
== FLT_MAX
|| scale_max
== FLT_MAX
)
10079 float v_min
= FLT_MAX
;
10080 float v_max
= -FLT_MAX
;
10081 for (int i
= 0; i
< values_count
; i
++)
10083 const float v
= values_getter(data
, i
);
10084 v_min
= ImMin(v_min
, v
);
10085 v_max
= ImMax(v_max
, v
);
10087 if (scale_min
== FLT_MAX
)
10089 if (scale_max
== FLT_MAX
)
10093 RenderFrame(frame_bb
.Min
, frame_bb
.Max
, GetColorU32(ImGuiCol_FrameBg
), true, style
.FrameRounding
);
10095 if (values_count
> 0)
10097 int res_w
= ImMin((int)graph_size
.x
, values_count
) + ((plot_type
== ImGuiPlotType_Lines
) ? -1 : 0);
10098 int item_count
= values_count
+ ((plot_type
== ImGuiPlotType_Lines
) ? -1 : 0);
10100 // Tooltip on hover
10101 int v_hovered
= -1;
10104 const float t
= ImClamp((g
.IO
.MousePos
.x
- inner_bb
.Min
.x
) / (inner_bb
.Max
.x
- inner_bb
.Min
.x
), 0.0f
, 0.9999f
);
10105 const int v_idx
= (int)(t
* item_count
);
10106 IM_ASSERT(v_idx
>= 0 && v_idx
< values_count
);
10108 const float v0
= values_getter(data
, (v_idx
+ values_offset
) % values_count
);
10109 const float v1
= values_getter(data
, (v_idx
+ 1 + values_offset
) % values_count
);
10110 if (plot_type
== ImGuiPlotType_Lines
)
10111 SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx
, v0
, v_idx
+1, v1
);
10112 else if (plot_type
== ImGuiPlotType_Histogram
)
10113 SetTooltip("%d: %8.4g", v_idx
, v0
);
10117 const float t_step
= 1.0f
/ (float)res_w
;
10118 const float inv_scale
= (scale_min
== scale_max
) ? 0.0f
: (1.0f
/ (scale_max
- scale_min
));
10120 float v0
= values_getter(data
, (0 + values_offset
) % values_count
);
10122 ImVec2 tp0
= ImVec2( t0
, 1.0f
- ImSaturate((v0
- scale_min
) * inv_scale
) ); // Point in the normalized space of our target rectangle
10123 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
10125 const ImU32 col_base
= GetColorU32((plot_type
== ImGuiPlotType_Lines
) ? ImGuiCol_PlotLines
: ImGuiCol_PlotHistogram
);
10126 const ImU32 col_hovered
= GetColorU32((plot_type
== ImGuiPlotType_Lines
) ? ImGuiCol_PlotLinesHovered
: ImGuiCol_PlotHistogramHovered
);
10128 for (int n
= 0; n
< res_w
; n
++)
10130 const float t1
= t0
+ t_step
;
10131 const int v1_idx
= (int)(t0
* item_count
+ 0.5f
);
10132 IM_ASSERT(v1_idx
>= 0 && v1_idx
< values_count
);
10133 const float v1
= values_getter(data
, (v1_idx
+ values_offset
+ 1) % values_count
);
10134 const ImVec2 tp1
= ImVec2( t1
, 1.0f
- ImSaturate((v1
- scale_min
) * inv_scale
) );
10136 // 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.
10137 ImVec2 pos0
= ImLerp(inner_bb
.Min
, inner_bb
.Max
, tp0
);
10138 ImVec2 pos1
= ImLerp(inner_bb
.Min
, inner_bb
.Max
, (plot_type
== ImGuiPlotType_Lines
) ? tp1
: ImVec2(tp1
.x
, histogram_zero_line_t
));
10139 if (plot_type
== ImGuiPlotType_Lines
)
10141 window
->DrawList
->AddLine(pos0
, pos1
, v_hovered
== v1_idx
? col_hovered
: col_base
);
10143 else if (plot_type
== ImGuiPlotType_Histogram
)
10145 if (pos1
.x
>= pos0
.x
+ 2.0f
)
10147 window
->DrawList
->AddRectFilled(pos0
, pos1
, v_hovered
== v1_idx
? col_hovered
: col_base
);
10157 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
));
10159 if (label_size
.x
> 0.0f
)
10160 RenderText(ImVec2(frame_bb
.Max
.x
+ style
.ItemInnerSpacing
.x
, inner_bb
.Min
.y
), label
);
10163 struct ImGuiPlotArrayGetterData
10165 const float* Values
;
10168 ImGuiPlotArrayGetterData(const float* values
, int stride
) { Values
= values
; Stride
= stride
; }
10171 static float Plot_ArrayGetter(void* data
, int idx
)
10173 ImGuiPlotArrayGetterData
* plot_data
= (ImGuiPlotArrayGetterData
*)data
;
10174 const float v
= *(const float*)(const void*)((const unsigned char*)plot_data
->Values
+ (size_t)idx
* plot_data
->Stride
);
10178 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
)
10180 ImGuiPlotArrayGetterData
data(values
, stride
);
10181 PlotEx(ImGuiPlotType_Lines
, label
, &Plot_ArrayGetter
, (void*)&data
, values_count
, values_offset
, overlay_text
, scale_min
, scale_max
, graph_size
);
10184 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
)
10186 PlotEx(ImGuiPlotType_Lines
, label
, values_getter
, data
, values_count
, values_offset
, overlay_text
, scale_min
, scale_max
, graph_size
);
10189 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
)
10191 ImGuiPlotArrayGetterData
data(values
, stride
);
10192 PlotEx(ImGuiPlotType_Histogram
, label
, &Plot_ArrayGetter
, (void*)&data
, values_count
, values_offset
, overlay_text
, scale_min
, scale_max
, graph_size
);
10195 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
)
10197 PlotEx(ImGuiPlotType_Histogram
, label
, values_getter
, data
, values_count
, values_offset
, overlay_text
, scale_min
, scale_max
, graph_size
);
10200 // size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size
10201 void ImGui::ProgressBar(float fraction
, const ImVec2
& size_arg
, const char* overlay
)
10203 ImGuiWindow
* window
= GetCurrentWindow();
10204 if (window
->SkipItems
)
10207 ImGuiContext
& g
= *GImGui
;
10208 const ImGuiStyle
& style
= g
.Style
;
10210 ImVec2 pos
= window
->DC
.CursorPos
;
10211 ImRect
bb(pos
, pos
+ CalcItemSize(size_arg
, CalcItemWidth(), g
.FontSize
+ style
.FramePadding
.y
*2.0f
));
10212 ItemSize(bb
, style
.FramePadding
.y
);
10213 if (!ItemAdd(bb
, 0))
10217 fraction
= ImSaturate(fraction
);
10218 RenderFrame(bb
.Min
, bb
.Max
, GetColorU32(ImGuiCol_FrameBg
), true, style
.FrameRounding
);
10219 bb
.Expand(ImVec2(-style
.FrameBorderSize
, -style
.FrameBorderSize
));
10220 const ImVec2 fill_br
= ImVec2(ImLerp(bb
.Min
.x
, bb
.Max
.x
, fraction
), bb
.Max
.y
);
10221 RenderRectFilledRangeH(window
->DrawList
, bb
, GetColorU32(ImGuiCol_PlotHistogram
), 0.0f
, fraction
, style
.FrameRounding
);
10223 // Default displaying the fraction as percentage string, but user can override it
10224 char overlay_buf
[32];
10227 ImFormatString(overlay_buf
, IM_ARRAYSIZE(overlay_buf
), "%.0f%%", fraction
*100+0.01f
);
10228 overlay
= overlay_buf
;
10231 ImVec2 overlay_size
= CalcTextSize(overlay
, NULL
);
10232 if (overlay_size
.x
> 0.0f
)
10233 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
);
10236 bool ImGui::Checkbox(const char* label
, bool* v
)
10238 ImGuiWindow
* window
= GetCurrentWindow();
10239 if (window
->SkipItems
)
10242 ImGuiContext
& g
= *GImGui
;
10243 const ImGuiStyle
& style
= g
.Style
;
10244 const ImGuiID id
= window
->GetID(label
);
10245 const ImVec2 label_size
= CalcTextSize(label
, NULL
, true);
10247 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
10248 ItemSize(check_bb
, style
.FramePadding
.y
);
10250 ImRect total_bb
= check_bb
;
10251 if (label_size
.x
> 0)
10252 SameLine(0, style
.ItemInnerSpacing
.x
);
10253 const ImRect
text_bb(window
->DC
.CursorPos
+ ImVec2(0,style
.FramePadding
.y
), window
->DC
.CursorPos
+ ImVec2(0,style
.FramePadding
.y
) + label_size
);
10254 if (label_size
.x
> 0)
10256 ItemSize(ImVec2(text_bb
.GetWidth(), check_bb
.GetHeight()), style
.FramePadding
.y
);
10257 total_bb
= ImRect(ImMin(check_bb
.Min
, text_bb
.Min
), ImMax(check_bb
.Max
, text_bb
.Max
));
10260 if (!ItemAdd(total_bb
, id
))
10263 bool hovered
, held
;
10264 bool pressed
= ButtonBehavior(total_bb
, id
, &hovered
, &held
);
10268 MarkItemValueChanged(id
);
10271 RenderNavHighlight(total_bb
, id
);
10272 RenderFrame(check_bb
.Min
, check_bb
.Max
, GetColorU32((held
&& hovered
) ? ImGuiCol_FrameBgActive
: hovered
? ImGuiCol_FrameBgHovered
: ImGuiCol_FrameBg
), true, style
.FrameRounding
);
10275 const float check_sz
= ImMin(check_bb
.GetWidth(), check_bb
.GetHeight());
10276 const float pad
= ImMax(1.0f
, (float)(int)(check_sz
/ 6.0f
));
10277 RenderCheckMark(check_bb
.Min
+ ImVec2(pad
,pad
), GetColorU32(ImGuiCol_CheckMark
), check_bb
.GetWidth() - pad
*2.0f
);
10281 LogRenderedText(&text_bb
.Min
, *v
? "[x]" : "[ ]");
10282 if (label_size
.x
> 0.0f
)
10283 RenderText(text_bb
.Min
, label
);
10288 bool ImGui::CheckboxFlags(const char* label
, unsigned int* flags
, unsigned int flags_value
)
10290 bool v
= ((*flags
& flags_value
) == flags_value
);
10291 bool pressed
= Checkbox(label
, &v
);
10295 *flags
|= flags_value
;
10297 *flags
&= ~flags_value
;
10303 bool ImGui::RadioButton(const char* label
, bool active
)
10305 ImGuiWindow
* window
= GetCurrentWindow();
10306 if (window
->SkipItems
)
10309 ImGuiContext
& g
= *GImGui
;
10310 const ImGuiStyle
& style
= g
.Style
;
10311 const ImGuiID id
= window
->GetID(label
);
10312 const ImVec2 label_size
= CalcTextSize(label
, NULL
, true);
10314 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));
10315 ItemSize(check_bb
, style
.FramePadding
.y
);
10317 ImRect total_bb
= check_bb
;
10318 if (label_size
.x
> 0)
10319 SameLine(0, style
.ItemInnerSpacing
.x
);
10320 const ImRect
text_bb(window
->DC
.CursorPos
+ ImVec2(0, style
.FramePadding
.y
), window
->DC
.CursorPos
+ ImVec2(0, style
.FramePadding
.y
) + label_size
);
10321 if (label_size
.x
> 0)
10323 ItemSize(ImVec2(text_bb
.GetWidth(), check_bb
.GetHeight()), style
.FramePadding
.y
);
10324 total_bb
.Add(text_bb
);
10327 if (!ItemAdd(total_bb
, id
))
10330 ImVec2 center
= check_bb
.GetCenter();
10331 center
.x
= (float)(int)center
.x
+ 0.5f
;
10332 center
.y
= (float)(int)center
.y
+ 0.5f
;
10333 const float radius
= check_bb
.GetHeight() * 0.5f
;
10335 bool hovered
, held
;
10336 bool pressed
= ButtonBehavior(total_bb
, id
, &hovered
, &held
);
10338 MarkItemValueChanged(id
);
10340 RenderNavHighlight(total_bb
, id
);
10341 window
->DrawList
->AddCircleFilled(center
, radius
, GetColorU32((held
&& hovered
) ? ImGuiCol_FrameBgActive
: hovered
? ImGuiCol_FrameBgHovered
: ImGuiCol_FrameBg
), 16);
10344 const float check_sz
= ImMin(check_bb
.GetWidth(), check_bb
.GetHeight());
10345 const float pad
= ImMax(1.0f
, (float)(int)(check_sz
/ 6.0f
));
10346 window
->DrawList
->AddCircleFilled(center
, radius
-pad
, GetColorU32(ImGuiCol_CheckMark
), 16);
10349 if (style
.FrameBorderSize
> 0.0f
)
10351 window
->DrawList
->AddCircle(center
+ImVec2(1,1), radius
, GetColorU32(ImGuiCol_BorderShadow
), 16, style
.FrameBorderSize
);
10352 window
->DrawList
->AddCircle(center
, radius
, GetColorU32(ImGuiCol_Border
), 16, style
.FrameBorderSize
);
10356 LogRenderedText(&text_bb
.Min
, active
? "(x)" : "( )");
10357 if (label_size
.x
> 0.0f
)
10358 RenderText(text_bb
.Min
, label
);
10363 bool ImGui::RadioButton(const char* label
, int* v
, int v_button
)
10365 const bool pressed
= RadioButton(label
, *v
== v_button
);
10371 static int InputTextCalcTextLenAndLineCount(const char* text_begin
, const char** out_text_end
)
10373 int line_count
= 0;
10374 const char* s
= text_begin
;
10375 while (char c
= *s
++) // We are only matching for \n so we can ignore UTF-8 decoding
10379 if (s
[0] != '\n' && s
[0] != '\r')
10385 static ImVec2
InputTextCalcTextSizeW(const ImWchar
* text_begin
, const ImWchar
* text_end
, const ImWchar
** remaining
, ImVec2
* out_offset
, bool stop_on_new_line
)
10387 ImFont
* font
= GImGui
->Font
;
10388 const float line_height
= GImGui
->FontSize
;
10389 const float scale
= line_height
/ font
->FontSize
;
10391 ImVec2 text_size
= ImVec2(0,0);
10392 float line_width
= 0.0f
;
10394 const ImWchar
* s
= text_begin
;
10395 while (s
< text_end
)
10397 unsigned int c
= (unsigned int)(*s
++);
10400 text_size
.x
= ImMax(text_size
.x
, line_width
);
10401 text_size
.y
+= line_height
;
10403 if (stop_on_new_line
)
10410 const float char_width
= font
->GetCharAdvance((unsigned short)c
) * scale
;
10411 line_width
+= char_width
;
10414 if (text_size
.x
< line_width
)
10415 text_size
.x
= line_width
;
10418 *out_offset
= ImVec2(line_width
, text_size
.y
+ line_height
); // offset allow for the possibility of sitting after a trailing \n
10420 if (line_width
> 0 || text_size
.y
== 0.0f
) // whereas size.y will ignore the trailing \n
10421 text_size
.y
+= line_height
;
10429 // 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)
10433 static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING
* obj
) { return obj
->CurLenW
; }
10434 static ImWchar
STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING
* obj
, int idx
) { return obj
->Text
[idx
]; }
10435 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
); }
10436 static int STB_TEXTEDIT_KEYTOTEXT(int key
) { return key
>= 0x10000 ? 0 : key
; }
10437 static ImWchar STB_TEXTEDIT_NEWLINE
= '\n';
10438 static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow
* r
, STB_TEXTEDIT_STRING
* obj
, int line_start_idx
)
10440 const ImWchar
* text
= obj
->Text
.Data
;
10441 const ImWchar
* text_remaining
= NULL
;
10442 const ImVec2 size
= InputTextCalcTextSizeW(text
+ line_start_idx
, text
+ obj
->CurLenW
, &text_remaining
, NULL
, true);
10445 r
->baseline_y_delta
= size
.y
;
10448 r
->num_chars
= (int)(text_remaining
- (text
+ line_start_idx
));
10451 static bool is_separator(unsigned int c
) { return ImCharIsBlankW(c
) || c
==',' || c
==';' || c
=='(' || c
==')' || c
=='{' || c
=='}' || c
=='[' || c
==']' || c
=='|'; }
10452 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; }
10453 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
; }
10454 #ifdef __APPLE__ // FIXME: Move setting to IO structure
10455 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; }
10456 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
; }
10458 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
; }
10460 #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h
10461 #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
10463 static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING
* obj
, int pos
, int n
)
10465 ImWchar
* dst
= obj
->Text
.Data
+ pos
;
10467 // We maintain our buffer length in both UTF-8 and wchar formats
10468 obj
->CurLenA
-= ImTextCountUtf8BytesFromStr(dst
, dst
+ n
);
10471 // Offset remaining text
10472 const ImWchar
* src
= obj
->Text
.Data
+ pos
+ n
;
10473 while (ImWchar c
= *src
++)
10478 static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING
* obj
, int pos
, const ImWchar
* new_text
, int new_text_len
)
10480 const int text_len
= obj
->CurLenW
;
10481 IM_ASSERT(pos
<= text_len
);
10482 if (new_text_len
+ text_len
+ 1 > obj
->Text
.Size
)
10485 const int new_text_len_utf8
= ImTextCountUtf8BytesFromStr(new_text
, new_text
+ new_text_len
);
10486 if (new_text_len_utf8
+ obj
->CurLenA
+ 1 > obj
->BufSizeA
)
10489 ImWchar
* text
= obj
->Text
.Data
;
10490 if (pos
!= text_len
)
10491 memmove(text
+ pos
+ new_text_len
, text
+ pos
, (size_t)(text_len
- pos
) * sizeof(ImWchar
));
10492 memcpy(text
+ pos
, new_text
, (size_t)new_text_len
* sizeof(ImWchar
));
10494 obj
->CurLenW
+= new_text_len
;
10495 obj
->CurLenA
+= new_text_len_utf8
;
10496 obj
->Text
[obj
->CurLenW
] = '\0';
10501 // 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)
10502 #define STB_TEXTEDIT_K_LEFT 0x10000 // keyboard input to move cursor left
10503 #define STB_TEXTEDIT_K_RIGHT 0x10001 // keyboard input to move cursor right
10504 #define STB_TEXTEDIT_K_UP 0x10002 // keyboard input to move cursor up
10505 #define STB_TEXTEDIT_K_DOWN 0x10003 // keyboard input to move cursor down
10506 #define STB_TEXTEDIT_K_LINESTART 0x10004 // keyboard input to move cursor to start of line
10507 #define STB_TEXTEDIT_K_LINEEND 0x10005 // keyboard input to move cursor to end of line
10508 #define STB_TEXTEDIT_K_TEXTSTART 0x10006 // keyboard input to move cursor to start of text
10509 #define STB_TEXTEDIT_K_TEXTEND 0x10007 // keyboard input to move cursor to end of text
10510 #define STB_TEXTEDIT_K_DELETE 0x10008 // keyboard input to delete selection or character under cursor
10511 #define STB_TEXTEDIT_K_BACKSPACE 0x10009 // keyboard input to delete selection or character left of cursor
10512 #define STB_TEXTEDIT_K_UNDO 0x1000A // keyboard input to perform undo
10513 #define STB_TEXTEDIT_K_REDO 0x1000B // keyboard input to perform redo
10514 #define STB_TEXTEDIT_K_WORDLEFT 0x1000C // keyboard input to move cursor left one word
10515 #define STB_TEXTEDIT_K_WORDRIGHT 0x1000D // keyboard input to move cursor right one word
10516 #define STB_TEXTEDIT_K_SHIFT 0x20000
10518 #define STB_TEXTEDIT_IMPLEMENTATION
10519 #include "stb_textedit.h"
10523 void ImGuiTextEditState::OnKeyPressed(int key
)
10525 stb_textedit_key(this, &StbState
, key
);
10526 CursorFollow
= true;
10530 // Public API to manipulate UTF-8 text
10531 // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)
10532 // FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
10533 void ImGuiTextEditCallbackData::DeleteChars(int pos
, int bytes_count
)
10535 IM_ASSERT(pos
+ bytes_count
<= BufTextLen
);
10536 char* dst
= Buf
+ pos
;
10537 const char* src
= Buf
+ pos
+ bytes_count
;
10538 while (char c
= *src
++)
10542 if (CursorPos
+ bytes_count
>= pos
)
10543 CursorPos
-= bytes_count
;
10544 else if (CursorPos
>= pos
)
10546 SelectionStart
= SelectionEnd
= CursorPos
;
10548 BufTextLen
-= bytes_count
;
10551 void ImGuiTextEditCallbackData::InsertChars(int pos
, const char* new_text
, const char* new_text_end
)
10553 const int new_text_len
= new_text_end
? (int)(new_text_end
- new_text
) : (int)strlen(new_text
);
10554 if (new_text_len
+ BufTextLen
+ 1 >= BufSize
)
10557 if (BufTextLen
!= pos
)
10558 memmove(Buf
+ pos
+ new_text_len
, Buf
+ pos
, (size_t)(BufTextLen
- pos
));
10559 memcpy(Buf
+ pos
, new_text
, (size_t)new_text_len
* sizeof(char));
10560 Buf
[BufTextLen
+ new_text_len
] = '\0';
10562 if (CursorPos
>= pos
)
10563 CursorPos
+= new_text_len
;
10564 SelectionStart
= SelectionEnd
= CursorPos
;
10566 BufTextLen
+= new_text_len
;
10569 // Return false to discard a character.
10570 static bool InputTextFilterCharacter(unsigned int* p_char
, ImGuiInputTextFlags flags
, ImGuiTextEditCallback callback
, void* user_data
)
10572 unsigned int c
= *p_char
;
10574 if (c
< 128 && c
!= ' ' && !isprint((int)(c
& 0xFF)))
10577 pass
|= (c
== '\n' && (flags
& ImGuiInputTextFlags_Multiline
));
10578 pass
|= (c
== '\t' && (flags
& ImGuiInputTextFlags_AllowTabInput
));
10583 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.
10586 if (flags
& (ImGuiInputTextFlags_CharsDecimal
| ImGuiInputTextFlags_CharsHexadecimal
| ImGuiInputTextFlags_CharsUppercase
| ImGuiInputTextFlags_CharsNoBlank
| ImGuiInputTextFlags_CharsScientific
))
10588 if (flags
& ImGuiInputTextFlags_CharsDecimal
)
10589 if (!(c
>= '0' && c
<= '9') && (c
!= '.') && (c
!= '-') && (c
!= '+') && (c
!= '*') && (c
!= '/'))
10592 if (flags
& ImGuiInputTextFlags_CharsScientific
)
10593 if (!(c
>= '0' && c
<= '9') && (c
!= '.') && (c
!= '-') && (c
!= '+') && (c
!= '*') && (c
!= '/') && (c
!= 'e') && (c
!= 'E'))
10596 if (flags
& ImGuiInputTextFlags_CharsHexadecimal
)
10597 if (!(c
>= '0' && c
<= '9') && !(c
>= 'a' && c
<= 'f') && !(c
>= 'A' && c
<= 'F'))
10600 if (flags
& ImGuiInputTextFlags_CharsUppercase
)
10601 if (c
>= 'a' && c
<= 'z')
10602 *p_char
= (c
+= (unsigned int)('A'-'a'));
10604 if (flags
& ImGuiInputTextFlags_CharsNoBlank
)
10605 if (ImCharIsBlankW(c
))
10609 if (flags
& ImGuiInputTextFlags_CallbackCharFilter
)
10611 ImGuiTextEditCallbackData callback_data
;
10612 memset(&callback_data
, 0, sizeof(ImGuiTextEditCallbackData
));
10613 callback_data
.EventFlag
= ImGuiInputTextFlags_CallbackCharFilter
;
10614 callback_data
.EventChar
= (ImWchar
)c
;
10615 callback_data
.Flags
= flags
;
10616 callback_data
.UserData
= user_data
;
10617 if (callback(&callback_data
) != 0)
10619 *p_char
= callback_data
.EventChar
;
10620 if (!callback_data
.EventChar
)
10627 // Edit a string of text
10628 // 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.
10629 // 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
10630 bool ImGui::InputTextEx(const char* label
, char* buf
, int buf_size
, const ImVec2
& size_arg
, ImGuiInputTextFlags flags
, ImGuiTextEditCallback callback
, void* user_data
)
10632 ImGuiWindow
* window
= GetCurrentWindow();
10633 if (window
->SkipItems
)
10636 IM_ASSERT(!((flags
& ImGuiInputTextFlags_CallbackHistory
) && (flags
& ImGuiInputTextFlags_Multiline
))); // Can't use both together (they both use up/down keys)
10637 IM_ASSERT(!((flags
& ImGuiInputTextFlags_CallbackCompletion
) && (flags
& ImGuiInputTextFlags_AllowTabInput
))); // Can't use both together (they both use tab key)
10639 ImGuiContext
& g
= *GImGui
;
10640 const ImGuiIO
& io
= g
.IO
;
10641 const ImGuiStyle
& style
= g
.Style
;
10643 const bool is_multiline
= (flags
& ImGuiInputTextFlags_Multiline
) != 0;
10644 const bool is_editable
= (flags
& ImGuiInputTextFlags_ReadOnly
) == 0;
10645 const bool is_password
= (flags
& ImGuiInputTextFlags_Password
) != 0;
10646 const bool is_undoable
= (flags
& ImGuiInputTextFlags_NoUndoRedo
) == 0;
10648 if (is_multiline
) // Open group before calling GetID() because groups tracks id created within their scope,
10650 const ImGuiID id
= window
->GetID(label
);
10651 const ImVec2 label_size
= CalcTextSize(label
, NULL
, true);
10652 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
10653 const ImRect
frame_bb(window
->DC
.CursorPos
, window
->DC
.CursorPos
+ size
);
10654 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
));
10656 ImGuiWindow
* draw_window
= window
;
10659 ItemAdd(total_bb
, id
, &frame_bb
);
10660 if (!BeginChildFrame(id
, frame_bb
.GetSize()))
10666 draw_window
= GetCurrentWindow();
10667 draw_window
->DC
.NavLayerActiveMaskNext
|= draw_window
->DC
.NavLayerCurrentMask
; // This is to ensure that EndChild() will display a navigation highlight
10668 size
.x
-= draw_window
->ScrollbarSizes
.x
;
10672 ItemSize(total_bb
, style
.FramePadding
.y
);
10673 if (!ItemAdd(total_bb
, id
, &frame_bb
))
10676 const bool hovered
= ItemHoverable(frame_bb
, id
);
10678 g
.MouseCursor
= ImGuiMouseCursor_TextInput
;
10680 // Password pushes a temporary font with only a fallback glyph
10683 const ImFontGlyph
* glyph
= g
.Font
->FindGlyph('*');
10684 ImFont
* password_font
= &g
.InputTextPasswordFont
;
10685 password_font
->FontSize
= g
.Font
->FontSize
;
10686 password_font
->Scale
= g
.Font
->Scale
;
10687 password_font
->DisplayOffset
= g
.Font
->DisplayOffset
;
10688 password_font
->Ascent
= g
.Font
->Ascent
;
10689 password_font
->Descent
= g
.Font
->Descent
;
10690 password_font
->ContainerAtlas
= g
.Font
->ContainerAtlas
;
10691 password_font
->FallbackGlyph
= glyph
;
10692 password_font
->FallbackAdvanceX
= glyph
->AdvanceX
;
10693 IM_ASSERT(password_font
->Glyphs
.empty() && password_font
->IndexAdvanceX
.empty() && password_font
->IndexLookup
.empty());
10694 PushFont(password_font
);
10697 // NB: we are only allowed to access 'edit_state' if we are the active widget.
10698 ImGuiTextEditState
& edit_state
= g
.InputTextState
;
10700 const bool focus_requested
= FocusableItemRegister(window
, id
, (flags
& (ImGuiInputTextFlags_CallbackCompletion
|ImGuiInputTextFlags_AllowTabInput
)) == 0); // Using completion callback disable keyboard tabbing
10701 const bool focus_requested_by_code
= focus_requested
&& (window
->FocusIdxAllCounter
== window
->FocusIdxAllRequestCurrent
);
10702 const bool focus_requested_by_tab
= focus_requested
&& !focus_requested_by_code
;
10704 const bool user_clicked
= hovered
&& io
.MouseClicked
[0];
10705 const bool user_scrolled
= is_multiline
&& g
.ActiveId
== 0 && edit_state
.Id
== id
&& g
.ActiveIdPreviousFrame
== draw_window
->GetIDNoKeepAlive("#SCROLLY");
10706 const bool user_nav_input_start
= (g
.ActiveId
!= id
) && ((g
.NavInputId
== id
) || (g
.NavActivateId
== id
&& g
.NavInputSource
== ImGuiInputSource_NavKeyboard
));
10708 bool clear_active_id
= false;
10710 bool select_all
= (g
.ActiveId
!= id
) && ((flags
& ImGuiInputTextFlags_AutoSelectAll
) != 0 || user_nav_input_start
) && (!is_multiline
);
10711 if (focus_requested
|| user_clicked
|| user_scrolled
|| user_nav_input_start
)
10713 if (g
.ActiveId
!= id
)
10716 // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)
10717 // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode)
10718 const int prev_len_w
= edit_state
.CurLenW
;
10719 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.
10720 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.
10721 ImStrncpy(edit_state
.InitialText
.Data
, buf
, edit_state
.InitialText
.Size
);
10722 const char* buf_end
= NULL
;
10723 edit_state
.CurLenW
= ImTextStrFromUtf8(edit_state
.Text
.Data
, edit_state
.Text
.Size
, buf
, NULL
, &buf_end
);
10724 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.
10725 edit_state
.CursorAnimReset();
10727 // Preserve cursor position and undo/redo stack if we come back to same widget
10728 // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar).
10729 const bool recycle_state
= (edit_state
.Id
== id
) && (prev_len_w
== edit_state
.CurLenW
);
10732 // Recycle existing cursor/selection/undo stack but clamp position
10733 // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
10734 edit_state
.CursorClamp();
10738 edit_state
.Id
= id
;
10739 edit_state
.ScrollX
= 0.0f
;
10740 stb_textedit_initialize_state(&edit_state
.StbState
, !is_multiline
);
10741 if (!is_multiline
&& focus_requested_by_code
)
10744 if (flags
& ImGuiInputTextFlags_AlwaysInsertMode
)
10745 edit_state
.StbState
.insert_mode
= true;
10746 if (!is_multiline
&& (focus_requested_by_tab
|| (user_clicked
&& io
.KeyCtrl
)))
10749 SetActiveID(id
, window
);
10750 SetFocusID(id
, window
);
10751 FocusWindow(window
);
10752 if (!is_multiline
&& !(flags
& ImGuiInputTextFlags_CallbackHistory
))
10753 g
.ActiveIdAllowNavDirFlags
|= ((1 << ImGuiDir_Up
) | (1 << ImGuiDir_Down
));
10755 else if (io
.MouseClicked
[0])
10757 // Release focus when we click outside
10758 clear_active_id
= true;
10761 bool value_changed
= false;
10762 bool enter_pressed
= false;
10764 if (g
.ActiveId
== id
)
10766 if (!is_editable
&& !g
.ActiveIdIsJustActivated
)
10768 // When read-only we always use the live data passed to the function
10769 edit_state
.Text
.resize(buf_size
+1);
10770 const char* buf_end
= NULL
;
10771 edit_state
.CurLenW
= ImTextStrFromUtf8(edit_state
.Text
.Data
, edit_state
.Text
.Size
, buf
, NULL
, &buf_end
);
10772 edit_state
.CurLenA
= (int)(buf_end
- buf
);
10773 edit_state
.CursorClamp();
10776 edit_state
.BufSizeA
= buf_size
;
10778 // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
10779 // Down the line we should have a cleaner library-wide concept of Selected vs Active.
10780 g
.ActiveIdAllowOverlap
= !io
.MouseDown
[0];
10781 g
.WantTextInputNextFrame
= 1;
10783 // Edit in progress
10784 const float mouse_x
= (io
.MousePos
.x
- frame_bb
.Min
.x
- style
.FramePadding
.x
) + edit_state
.ScrollX
;
10785 const float mouse_y
= (is_multiline
? (io
.MousePos
.y
- draw_window
->DC
.CursorPos
.y
- style
.FramePadding
.y
) : (g
.FontSize
*0.5f
));
10787 const bool is_osx
= io
.OptMacOSXBehaviors
;
10788 if (select_all
|| (hovered
&& !is_osx
&& io
.MouseDoubleClicked
[0]))
10790 edit_state
.SelectAll();
10791 edit_state
.SelectedAllMouseLock
= true;
10793 else if (hovered
&& is_osx
&& io
.MouseDoubleClicked
[0])
10795 // Double-click select a word only, OS X style (by simulating keystrokes)
10796 edit_state
.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT
);
10797 edit_state
.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT
| STB_TEXTEDIT_K_SHIFT
);
10799 else if (io
.MouseClicked
[0] && !edit_state
.SelectedAllMouseLock
)
10803 stb_textedit_click(&edit_state
, &edit_state
.StbState
, mouse_x
, mouse_y
);
10804 edit_state
.CursorAnimReset();
10807 else if (io
.MouseDown
[0] && !edit_state
.SelectedAllMouseLock
&& (io
.MouseDelta
.x
!= 0.0f
|| io
.MouseDelta
.y
!= 0.0f
))
10809 stb_textedit_drag(&edit_state
, &edit_state
.StbState
, mouse_x
, mouse_y
);
10810 edit_state
.CursorAnimReset();
10811 edit_state
.CursorFollow
= true;
10813 if (edit_state
.SelectedAllMouseLock
&& !io
.MouseDown
[0])
10814 edit_state
.SelectedAllMouseLock
= false;
10816 if (io
.InputCharacters
[0])
10818 // Process text input (before we check for Return because using some IME will effectively send a Return?)
10819 // 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.
10820 bool ignore_inputs
= (io
.KeyCtrl
&& !io
.KeyAlt
) || (is_osx
&& io
.KeySuper
);
10821 if (!ignore_inputs
&& is_editable
&& !user_nav_input_start
)
10822 for (int n
= 0; n
< IM_ARRAYSIZE(io
.InputCharacters
) && io
.InputCharacters
[n
]; n
++)
10824 // Insert character if they pass filtering
10825 unsigned int c
= (unsigned int)io
.InputCharacters
[n
];
10826 if (InputTextFilterCharacter(&c
, flags
, callback
, user_data
))
10827 edit_state
.OnKeyPressed((int)c
);
10830 // Consume characters
10831 memset(g
.IO
.InputCharacters
, 0, sizeof(g
.IO
.InputCharacters
));
10835 bool cancel_edit
= false;
10836 if (g
.ActiveId
== id
&& !g
.ActiveIdIsJustActivated
&& !clear_active_id
)
10838 // Handle key-presses
10839 const int k_mask
= (io
.KeyShift
? STB_TEXTEDIT_K_SHIFT
: 0);
10840 const bool is_osx
= io
.OptMacOSXBehaviors
;
10841 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
10842 const bool is_osx_shift_shortcut
= is_osx
&& io
.KeySuper
&& io
.KeyShift
&& !io
.KeyCtrl
&& !io
.KeyAlt
;
10843 const bool is_wordmove_key_down
= is_osx
? io
.KeyAlt
: io
.KeyCtrl
; // OS X style: Text editing cursor movement using Alt instead of Ctrl
10844 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
10845 const bool is_ctrl_key_only
= io
.KeyCtrl
&& !io
.KeyShift
&& !io
.KeyAlt
&& !io
.KeySuper
;
10846 const bool is_shift_key_only
= io
.KeyShift
&& !io
.KeyCtrl
&& !io
.KeyAlt
&& !io
.KeySuper
;
10848 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());
10849 const bool is_copy
= ((is_shortcut_key
&& IsKeyPressedMap(ImGuiKey_C
)) || (is_ctrl_key_only
&& IsKeyPressedMap(ImGuiKey_Insert
))) && !is_password
&& (!is_multiline
|| edit_state
.HasSelection());
10850 const bool is_paste
= ((is_shortcut_key
&& IsKeyPressedMap(ImGuiKey_V
)) || (is_shift_key_only
&& IsKeyPressedMap(ImGuiKey_Insert
))) && is_editable
;
10851 const bool is_undo
= ((is_shortcut_key
&& IsKeyPressedMap(ImGuiKey_Z
)) && is_editable
&& is_undoable
);
10852 const bool is_redo
= ((is_shortcut_key
&& IsKeyPressedMap(ImGuiKey_Y
)) || (is_osx_shift_shortcut
&& IsKeyPressedMap(ImGuiKey_Z
))) && is_editable
&& is_undoable
;
10854 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
); }
10855 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
); }
10856 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
); }
10857 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
); }
10858 else if (IsKeyPressedMap(ImGuiKey_Home
)) { edit_state
.OnKeyPressed(io
.KeyCtrl
? STB_TEXTEDIT_K_TEXTSTART
| k_mask
: STB_TEXTEDIT_K_LINESTART
| k_mask
); }
10859 else if (IsKeyPressedMap(ImGuiKey_End
)) { edit_state
.OnKeyPressed(io
.KeyCtrl
? STB_TEXTEDIT_K_TEXTEND
| k_mask
: STB_TEXTEDIT_K_LINEEND
| k_mask
); }
10860 else if (IsKeyPressedMap(ImGuiKey_Delete
) && is_editable
) { edit_state
.OnKeyPressed(STB_TEXTEDIT_K_DELETE
| k_mask
); }
10861 else if (IsKeyPressedMap(ImGuiKey_Backspace
) && is_editable
)
10863 if (!edit_state
.HasSelection())
10865 if (is_wordmove_key_down
) edit_state
.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT
|STB_TEXTEDIT_K_SHIFT
);
10866 else if (is_osx
&& io
.KeySuper
&& !io
.KeyAlt
&& !io
.KeyCtrl
) edit_state
.OnKeyPressed(STB_TEXTEDIT_K_LINESTART
|STB_TEXTEDIT_K_SHIFT
);
10868 edit_state
.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE
| k_mask
);
10870 else if (IsKeyPressedMap(ImGuiKey_Enter
))
10872 bool ctrl_enter_for_new_line
= (flags
& ImGuiInputTextFlags_CtrlEnterForNewLine
) != 0;
10873 if (!is_multiline
|| (ctrl_enter_for_new_line
&& !io
.KeyCtrl
) || (!ctrl_enter_for_new_line
&& io
.KeyCtrl
))
10875 enter_pressed
= clear_active_id
= true;
10877 else if (is_editable
)
10879 unsigned int c
= '\n'; // Insert new line
10880 if (InputTextFilterCharacter(&c
, flags
, callback
, user_data
))
10881 edit_state
.OnKeyPressed((int)c
);
10884 else if ((flags
& ImGuiInputTextFlags_AllowTabInput
) && IsKeyPressedMap(ImGuiKey_Tab
) && !io
.KeyCtrl
&& !io
.KeyShift
&& !io
.KeyAlt
&& is_editable
)
10886 unsigned int c
= '\t'; // Insert TAB
10887 if (InputTextFilterCharacter(&c
, flags
, callback
, user_data
))
10888 edit_state
.OnKeyPressed((int)c
);
10890 else if (IsKeyPressedMap(ImGuiKey_Escape
))
10892 clear_active_id
= cancel_edit
= true;
10894 else if (is_undo
|| is_redo
)
10896 edit_state
.OnKeyPressed(is_undo
? STB_TEXTEDIT_K_UNDO
: STB_TEXTEDIT_K_REDO
);
10897 edit_state
.ClearSelection();
10899 else if (is_shortcut_key
&& IsKeyPressedMap(ImGuiKey_A
))
10901 edit_state
.SelectAll();
10902 edit_state
.CursorFollow
= true;
10904 else if (is_cut
|| is_copy
)
10907 if (io
.SetClipboardTextFn
)
10909 const int ib
= edit_state
.HasSelection() ? ImMin(edit_state
.StbState
.select_start
, edit_state
.StbState
.select_end
) : 0;
10910 const int ie
= edit_state
.HasSelection() ? ImMax(edit_state
.StbState
.select_start
, edit_state
.StbState
.select_end
) : edit_state
.CurLenW
;
10911 edit_state
.TempTextBuffer
.resize((ie
-ib
) * 4 + 1);
10912 ImTextStrToUtf8(edit_state
.TempTextBuffer
.Data
, edit_state
.TempTextBuffer
.Size
, edit_state
.Text
.Data
+ib
, edit_state
.Text
.Data
+ie
);
10913 SetClipboardText(edit_state
.TempTextBuffer
.Data
);
10917 if (!edit_state
.HasSelection())
10918 edit_state
.SelectAll();
10919 edit_state
.CursorFollow
= true;
10920 stb_textedit_cut(&edit_state
, &edit_state
.StbState
);
10925 if (const char* clipboard
= GetClipboardText())
10927 // Filter pasted buffer
10928 const int clipboard_len
= (int)strlen(clipboard
);
10929 ImWchar
* clipboard_filtered
= (ImWchar
*)ImGui::MemAlloc((clipboard_len
+1) * sizeof(ImWchar
));
10930 int clipboard_filtered_len
= 0;
10931 for (const char* s
= clipboard
; *s
; )
10934 s
+= ImTextCharFromUtf8(&c
, s
, NULL
);
10937 if (c
>= 0x10000 || !InputTextFilterCharacter(&c
, flags
, callback
, user_data
))
10939 clipboard_filtered
[clipboard_filtered_len
++] = (ImWchar
)c
;
10941 clipboard_filtered
[clipboard_filtered_len
] = 0;
10942 if (clipboard_filtered_len
> 0) // If everything was filtered, ignore the pasting operation
10944 stb_textedit_paste(&edit_state
, &edit_state
.StbState
, clipboard_filtered
, clipboard_filtered_len
);
10945 edit_state
.CursorFollow
= true;
10947 ImGui::MemFree(clipboard_filtered
);
10952 if (g
.ActiveId
== id
)
10956 // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents.
10957 if (is_editable
&& strncmp(buf
, edit_state
.InitialText
.Data
, buf_size
) != 0)
10959 ImStrncpy(buf
, edit_state
.InitialText
.Data
, buf_size
);
10960 value_changed
= true;
10964 // 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.
10965 // 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.
10966 bool apply_edit_back_to_user_buffer
= !cancel_edit
|| (enter_pressed
&& (flags
& ImGuiInputTextFlags_EnterReturnsTrue
) != 0);
10967 if (apply_edit_back_to_user_buffer
)
10969 // Apply new value immediately - copy modified buffer back
10970 // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer
10971 // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect.
10972 // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks.
10975 edit_state
.TempTextBuffer
.resize(edit_state
.Text
.Size
* 4);
10976 ImTextStrToUtf8(edit_state
.TempTextBuffer
.Data
, edit_state
.TempTextBuffer
.Size
, edit_state
.Text
.Data
, NULL
);
10980 if ((flags
& (ImGuiInputTextFlags_CallbackCompletion
| ImGuiInputTextFlags_CallbackHistory
| ImGuiInputTextFlags_CallbackAlways
)) != 0)
10982 IM_ASSERT(callback
!= NULL
);
10984 // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
10985 ImGuiInputTextFlags event_flag
= 0;
10986 ImGuiKey event_key
= ImGuiKey_COUNT
;
10987 if ((flags
& ImGuiInputTextFlags_CallbackCompletion
) != 0 && IsKeyPressedMap(ImGuiKey_Tab
))
10989 event_flag
= ImGuiInputTextFlags_CallbackCompletion
;
10990 event_key
= ImGuiKey_Tab
;
10992 else if ((flags
& ImGuiInputTextFlags_CallbackHistory
) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow
))
10994 event_flag
= ImGuiInputTextFlags_CallbackHistory
;
10995 event_key
= ImGuiKey_UpArrow
;
10997 else if ((flags
& ImGuiInputTextFlags_CallbackHistory
) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow
))
10999 event_flag
= ImGuiInputTextFlags_CallbackHistory
;
11000 event_key
= ImGuiKey_DownArrow
;
11002 else if (flags
& ImGuiInputTextFlags_CallbackAlways
)
11003 event_flag
= ImGuiInputTextFlags_CallbackAlways
;
11007 ImGuiTextEditCallbackData callback_data
;
11008 memset(&callback_data
, 0, sizeof(ImGuiTextEditCallbackData
));
11009 callback_data
.EventFlag
= event_flag
;
11010 callback_data
.Flags
= flags
;
11011 callback_data
.UserData
= user_data
;
11012 callback_data
.ReadOnly
= !is_editable
;
11014 callback_data
.EventKey
= event_key
;
11015 callback_data
.Buf
= edit_state
.TempTextBuffer
.Data
;
11016 callback_data
.BufTextLen
= edit_state
.CurLenA
;
11017 callback_data
.BufSize
= edit_state
.BufSizeA
;
11018 callback_data
.BufDirty
= false;
11020 // 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)
11021 ImWchar
* text
= edit_state
.Text
.Data
;
11022 const int utf8_cursor_pos
= callback_data
.CursorPos
= ImTextCountUtf8BytesFromStr(text
, text
+ edit_state
.StbState
.cursor
);
11023 const int utf8_selection_start
= callback_data
.SelectionStart
= ImTextCountUtf8BytesFromStr(text
, text
+ edit_state
.StbState
.select_start
);
11024 const int utf8_selection_end
= callback_data
.SelectionEnd
= ImTextCountUtf8BytesFromStr(text
, text
+ edit_state
.StbState
.select_end
);
11027 callback(&callback_data
);
11029 // Read back what user may have modified
11030 IM_ASSERT(callback_data
.Buf
== edit_state
.TempTextBuffer
.Data
); // Invalid to modify those fields
11031 IM_ASSERT(callback_data
.BufSize
== edit_state
.BufSizeA
);
11032 IM_ASSERT(callback_data
.Flags
== flags
);
11033 if (callback_data
.CursorPos
!= utf8_cursor_pos
) edit_state
.StbState
.cursor
= ImTextCountCharsFromUtf8(callback_data
.Buf
, callback_data
.Buf
+ callback_data
.CursorPos
);
11034 if (callback_data
.SelectionStart
!= utf8_selection_start
) edit_state
.StbState
.select_start
= ImTextCountCharsFromUtf8(callback_data
.Buf
, callback_data
.Buf
+ callback_data
.SelectionStart
);
11035 if (callback_data
.SelectionEnd
!= utf8_selection_end
) edit_state
.StbState
.select_end
= ImTextCountCharsFromUtf8(callback_data
.Buf
, callback_data
.Buf
+ callback_data
.SelectionEnd
);
11036 if (callback_data
.BufDirty
)
11038 IM_ASSERT(callback_data
.BufTextLen
== (int)strlen(callback_data
.Buf
)); // You need to maintain BufTextLen if you change the text!
11039 edit_state
.CurLenW
= ImTextStrFromUtf8(edit_state
.Text
.Data
, edit_state
.Text
.Size
, callback_data
.Buf
, NULL
);
11040 edit_state
.CurLenA
= callback_data
.BufTextLen
; // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
11041 edit_state
.CursorAnimReset();
11046 // Copy back to user buffer
11047 if (is_editable
&& strcmp(edit_state
.TempTextBuffer
.Data
, buf
) != 0)
11049 ImStrncpy(buf
, edit_state
.TempTextBuffer
.Data
, buf_size
);
11050 value_changed
= true;
11055 // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value)
11056 if (clear_active_id
&& g
.ActiveId
== id
)
11060 // 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.
11061 const char* buf_display
= (g
.ActiveId
== id
&& is_editable
) ? edit_state
.TempTextBuffer
.Data
: buf
; buf
= NULL
;
11065 RenderNavHighlight(frame_bb
, id
);
11066 RenderFrame(frame_bb
.Min
, frame_bb
.Max
, GetColorU32(ImGuiCol_FrameBg
), true, style
.FrameRounding
);
11069 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
11070 ImVec2 render_pos
= is_multiline
? draw_window
->DC
.CursorPos
: frame_bb
.Min
+ style
.FramePadding
;
11071 ImVec2
text_size(0.f
, 0.f
);
11072 const bool is_currently_scrolling
= (edit_state
.Id
== id
&& is_multiline
&& g
.ActiveId
== draw_window
->GetIDNoKeepAlive("#SCROLLY"));
11073 if (g
.ActiveId
== id
|| is_currently_scrolling
)
11075 edit_state
.CursorAnim
+= io
.DeltaTime
;
11077 // This is going to be messy. We need to:
11078 // - Display the text (this alone can be more easily clipped)
11079 // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
11080 // - Measure text height (for scrollbar)
11081 // 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)
11082 // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
11083 const ImWchar
* text_begin
= edit_state
.Text
.Data
;
11084 ImVec2 cursor_offset
, select_start_offset
;
11087 // Count lines + find lines numbers straddling 'cursor' and 'select_start' position.
11088 const ImWchar
* searches_input_ptr
[2];
11089 searches_input_ptr
[0] = text_begin
+ edit_state
.StbState
.cursor
;
11090 searches_input_ptr
[1] = NULL
;
11091 int searches_remaining
= 1;
11092 int searches_result_line_number
[2] = { -1, -999 };
11093 if (edit_state
.StbState
.select_start
!= edit_state
.StbState
.select_end
)
11095 searches_input_ptr
[1] = text_begin
+ ImMin(edit_state
.StbState
.select_start
, edit_state
.StbState
.select_end
);
11096 searches_result_line_number
[1] = -1;
11097 searches_remaining
++;
11100 // Iterate all lines to find our line numbers
11101 // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.
11102 searches_remaining
+= is_multiline
? 1 : 0;
11103 int line_count
= 0;
11104 for (const ImWchar
* s
= text_begin
; *s
!= 0; s
++)
11108 if (searches_result_line_number
[0] == -1 && s
>= searches_input_ptr
[0]) { searches_result_line_number
[0] = line_count
; if (--searches_remaining
<= 0) break; }
11109 if (searches_result_line_number
[1] == -1 && s
>= searches_input_ptr
[1]) { searches_result_line_number
[1] = line_count
; if (--searches_remaining
<= 0) break; }
11112 if (searches_result_line_number
[0] == -1) searches_result_line_number
[0] = line_count
;
11113 if (searches_result_line_number
[1] == -1) searches_result_line_number
[1] = line_count
;
11115 // Calculate 2d position by finding the beginning of the line and measuring distance
11116 cursor_offset
.x
= InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr
[0], text_begin
), searches_input_ptr
[0]).x
;
11117 cursor_offset
.y
= searches_result_line_number
[0] * g
.FontSize
;
11118 if (searches_result_line_number
[1] >= 0)
11120 select_start_offset
.x
= InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr
[1], text_begin
), searches_input_ptr
[1]).x
;
11121 select_start_offset
.y
= searches_result_line_number
[1] * g
.FontSize
;
11124 // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224)
11126 text_size
= ImVec2(size
.x
, line_count
* g
.FontSize
);
11130 if (edit_state
.CursorFollow
)
11132 // Horizontal scroll in chunks of quarter width
11133 if (!(flags
& ImGuiInputTextFlags_NoHorizontalScroll
))
11135 const float scroll_increment_x
= size
.x
* 0.25f
;
11136 if (cursor_offset
.x
< edit_state
.ScrollX
)
11137 edit_state
.ScrollX
= (float)(int)ImMax(0.0f
, cursor_offset
.x
- scroll_increment_x
);
11138 else if (cursor_offset
.x
- size
.x
>= edit_state
.ScrollX
)
11139 edit_state
.ScrollX
= (float)(int)(cursor_offset
.x
- size
.x
+ scroll_increment_x
);
11143 edit_state
.ScrollX
= 0.0f
;
11149 float scroll_y
= draw_window
->Scroll
.y
;
11150 if (cursor_offset
.y
- g
.FontSize
< scroll_y
)
11151 scroll_y
= ImMax(0.0f
, cursor_offset
.y
- g
.FontSize
);
11152 else if (cursor_offset
.y
- size
.y
>= scroll_y
)
11153 scroll_y
= cursor_offset
.y
- size
.y
;
11154 draw_window
->DC
.CursorPos
.y
+= (draw_window
->Scroll
.y
- scroll_y
); // To avoid a frame of lag
11155 draw_window
->Scroll
.y
= scroll_y
;
11156 render_pos
.y
= draw_window
->DC
.CursorPos
.y
;
11159 edit_state
.CursorFollow
= false;
11160 const ImVec2 render_scroll
= ImVec2(edit_state
.ScrollX
, 0.0f
);
11163 if (edit_state
.StbState
.select_start
!= edit_state
.StbState
.select_end
)
11165 const ImWchar
* text_selected_begin
= text_begin
+ ImMin(edit_state
.StbState
.select_start
, edit_state
.StbState
.select_end
);
11166 const ImWchar
* text_selected_end
= text_begin
+ ImMax(edit_state
.StbState
.select_start
, edit_state
.StbState
.select_end
);
11168 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.
11169 float bg_offy_dn
= is_multiline
? 0.0f
: 2.0f
;
11170 ImU32 bg_color
= GetColorU32(ImGuiCol_TextSelectedBg
);
11171 ImVec2 rect_pos
= render_pos
+ select_start_offset
- render_scroll
;
11172 for (const ImWchar
* p
= text_selected_begin
; p
< text_selected_end
; )
11174 if (rect_pos
.y
> clip_rect
.w
+ g
.FontSize
)
11176 if (rect_pos
.y
< clip_rect
.y
)
11178 while (p
< text_selected_end
)
11184 ImVec2 rect_size
= InputTextCalcTextSizeW(p
, text_selected_end
, &p
, NULL
, true);
11185 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
11186 ImRect
rect(rect_pos
+ ImVec2(0.0f
, bg_offy_up
- g
.FontSize
), rect_pos
+ImVec2(rect_size
.x
, bg_offy_dn
));
11187 rect
.ClipWith(clip_rect
);
11188 if (rect
.Overlaps(clip_rect
))
11189 draw_window
->DrawList
->AddRectFilled(rect
.Min
, rect
.Max
, bg_color
);
11191 rect_pos
.x
= render_pos
.x
- render_scroll
.x
;
11192 rect_pos
.y
+= g
.FontSize
;
11196 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
);
11198 // Draw blinking cursor
11199 bool cursor_is_visible
= (!g
.IO
.OptCursorBlink
) || (g
.InputTextState
.CursorAnim
<= 0.0f
) || ImFmod(g
.InputTextState
.CursorAnim
, 1.20f
) <= 0.80f
;
11200 ImVec2 cursor_screen_pos
= render_pos
+ cursor_offset
- render_scroll
;
11201 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
);
11202 if (cursor_is_visible
&& cursor_screen_rect
.Overlaps(clip_rect
))
11203 draw_window
->DrawList
->AddLine(cursor_screen_rect
.Min
, cursor_screen_rect
.GetBL(), GetColorU32(ImGuiCol_Text
));
11205 // 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.)
11207 g
.PlatformImePos
= ImVec2(cursor_screen_pos
.x
- 1, cursor_screen_pos
.y
- g
.FontSize
);
11211 // Render text only
11212 const char* buf_end
= NULL
;
11214 text_size
= ImVec2(size
.x
, InputTextCalcTextLenAndLineCount(buf_display
, &buf_end
) * g
.FontSize
); // We don't need width
11215 draw_window
->DrawList
->AddText(g
.Font
, g
.FontSize
, render_pos
, GetColorU32(ImGuiCol_Text
), buf_display
, buf_end
, 0.0f
, is_multiline
? NULL
: &clip_rect
);
11220 Dummy(text_size
+ ImVec2(0.0f
, g
.FontSize
)); // Always add room to scroll an extra line
11229 if (g
.LogEnabled
&& !is_password
)
11230 LogRenderedText(&render_pos
, buf_display
, NULL
);
11232 if (label_size
.x
> 0)
11233 RenderText(ImVec2(frame_bb
.Max
.x
+ style
.ItemInnerSpacing
.x
, frame_bb
.Min
.y
+ style
.FramePadding
.y
), label
);
11236 MarkItemValueChanged(id
);
11238 if ((flags
& ImGuiInputTextFlags_EnterReturnsTrue
) != 0)
11239 return enter_pressed
;
11241 return value_changed
;
11244 bool ImGui::InputText(const char* label
, char* buf
, size_t buf_size
, ImGuiInputTextFlags flags
, ImGuiTextEditCallback callback
, void* user_data
)
11246 IM_ASSERT(!(flags
& ImGuiInputTextFlags_Multiline
)); // call InputTextMultiline()
11247 return InputTextEx(label
, buf
, (int)buf_size
, ImVec2(0,0), flags
, callback
, user_data
);
11250 bool ImGui::InputTextMultiline(const char* label
, char* buf
, size_t buf_size
, const ImVec2
& size
, ImGuiInputTextFlags flags
, ImGuiTextEditCallback callback
, void* user_data
)
11252 return InputTextEx(label
, buf
, (int)buf_size
, size
, flags
| ImGuiInputTextFlags_Multiline
, callback
, user_data
);
11255 // NB: format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "format" argument)
11256 bool ImGui::InputScalar(const char* label
, ImGuiDataType data_type
, void* data_ptr
, const void* step
, const void* step_fast
, const char* format
, ImGuiInputTextFlags extra_flags
)
11258 ImGuiWindow
* window
= GetCurrentWindow();
11259 if (window
->SkipItems
)
11262 ImGuiContext
& g
= *GImGui
;
11263 const ImGuiStyle
& style
= g
.Style
;
11265 IM_ASSERT(data_type
>= 0 && data_type
< ImGuiDataType_COUNT
);
11266 if (format
== NULL
)
11267 format
= GDataTypeInfo
[data_type
].PrintFmt
;
11270 DataTypeFormatString(buf
, IM_ARRAYSIZE(buf
), data_type
, data_ptr
, format
);
11272 bool value_changed
= false;
11273 if ((extra_flags
& (ImGuiInputTextFlags_CharsHexadecimal
| ImGuiInputTextFlags_CharsScientific
)) == 0)
11274 extra_flags
|= ImGuiInputTextFlags_CharsDecimal
;
11275 extra_flags
|= ImGuiInputTextFlags_AutoSelectAll
;
11279 const float button_size
= GetFrameHeight();
11281 BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive()
11283 PushItemWidth(ImMax(1.0f
, CalcItemWidth() - (button_size
+ style
.ItemInnerSpacing
.x
) * 2));
11284 if (InputText("", buf
, IM_ARRAYSIZE(buf
), extra_flags
)) // PushId(label) + "" gives us the expected ID from outside point of view
11285 value_changed
= DataTypeApplyOpFromText(buf
, g
.InputTextState
.InitialText
.Data
, data_type
, data_ptr
, format
);
11289 SameLine(0, style
.ItemInnerSpacing
.x
);
11290 if (ButtonEx("-", ImVec2(button_size
, button_size
), ImGuiButtonFlags_Repeat
| ImGuiButtonFlags_DontClosePopups
))
11292 DataTypeApplyOp(data_type
, '-', data_ptr
, data_ptr
, g
.IO
.KeyCtrl
&& step_fast
? step_fast
: step
);
11293 value_changed
= true;
11295 SameLine(0, style
.ItemInnerSpacing
.x
);
11296 if (ButtonEx("+", ImVec2(button_size
, button_size
), ImGuiButtonFlags_Repeat
| ImGuiButtonFlags_DontClosePopups
))
11298 DataTypeApplyOp(data_type
, '+', data_ptr
, data_ptr
, g
.IO
.KeyCtrl
&& step_fast
? step_fast
: step
);
11299 value_changed
= true;
11301 SameLine(0, style
.ItemInnerSpacing
.x
);
11302 TextUnformatted(label
, FindRenderedTextEnd(label
));
11309 if (InputText(label
, buf
, IM_ARRAYSIZE(buf
), extra_flags
))
11310 value_changed
= DataTypeApplyOpFromText(buf
, g
.InputTextState
.InitialText
.Data
, data_type
, data_ptr
, format
);
11313 return value_changed
;
11316 bool ImGui::InputFloat(const char* label
, float* v
, float step
, float step_fast
, const char* format
, ImGuiInputTextFlags extra_flags
)
11318 extra_flags
|= ImGuiInputTextFlags_CharsScientific
;
11319 return InputScalar(label
, ImGuiDataType_Float
, (void*)v
, (void*)(step
>0.0f
? &step
: NULL
), (void*)(step_fast
>0.0f
? &step_fast
: NULL
), format
, extra_flags
);
11322 bool ImGui::InputDouble(const char* label
, double* v
, double step
, double step_fast
, const char* format
, ImGuiInputTextFlags extra_flags
)
11324 extra_flags
|= ImGuiInputTextFlags_CharsScientific
;
11325 return InputScalar(label
, ImGuiDataType_Double
, (void*)v
, (void*)(step
>0.0 ? &step
: NULL
), (void*)(step_fast
>0.0 ? &step_fast
: NULL
), format
, extra_flags
);
11328 bool ImGui::InputInt(const char* label
, int* v
, int step
, int step_fast
, ImGuiInputTextFlags extra_flags
)
11330 // 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.
11331 const char* format
= (extra_flags
& ImGuiInputTextFlags_CharsHexadecimal
) ? "%08X" : "%d";
11332 return InputScalar(label
, ImGuiDataType_S32
, (void*)v
, (void*)(step
>0 ? &step
: NULL
), (void*)(step_fast
>0 ? &step_fast
: NULL
), format
, extra_flags
);
11335 bool ImGui::InputScalarN(const char* label
, ImGuiDataType data_type
, void* v
, int components
, const void* step
, const void* step_fast
, const char* format
, ImGuiInputTextFlags extra_flags
)
11337 ImGuiWindow
* window
= GetCurrentWindow();
11338 if (window
->SkipItems
)
11341 ImGuiContext
& g
= *GImGui
;
11342 bool value_changed
= false;
11345 PushMultiItemsWidths(components
);
11346 size_t type_size
= GDataTypeInfo
[data_type
].Size
;
11347 for (int i
= 0; i
< components
; i
++)
11350 value_changed
|= InputScalar("##v", data_type
, v
, step
, step_fast
, format
, extra_flags
);
11351 SameLine(0, g
.Style
.ItemInnerSpacing
.x
);
11354 v
= (void*)((char*)v
+ type_size
);
11358 TextUnformatted(label
, FindRenderedTextEnd(label
));
11360 return value_changed
;
11363 bool ImGui::InputFloat2(const char* label
, float v
[2], const char* format
, ImGuiInputTextFlags extra_flags
)
11365 return InputScalarN(label
, ImGuiDataType_Float
, v
, 2, NULL
, NULL
, format
, extra_flags
);
11368 bool ImGui::InputFloat3(const char* label
, float v
[3], const char* format
, ImGuiInputTextFlags extra_flags
)
11370 return InputScalarN(label
, ImGuiDataType_Float
, v
, 3, NULL
, NULL
, format
, extra_flags
);
11373 bool ImGui::InputFloat4(const char* label
, float v
[4], const char* format
, ImGuiInputTextFlags extra_flags
)
11375 return InputScalarN(label
, ImGuiDataType_Float
, v
, 4, NULL
, NULL
, format
, extra_flags
);
11378 // Prefer using "const char* format" directly, which is more flexible and consistent with other API.
11379 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
11380 bool ImGui::InputFloat(const char* label
, float* v
, float step
, float step_fast
, int decimal_precision
, ImGuiInputTextFlags extra_flags
)
11382 char format
[16] = "%f";
11383 if (decimal_precision
>= 0)
11384 ImFormatString(format
, IM_ARRAYSIZE(format
), "%%.%df", decimal_precision
);
11385 return InputFloat(label
, v
, step
, step_fast
, format
, extra_flags
);
11388 bool ImGui::InputFloat2(const char* label
, float v
[2], int decimal_precision
, ImGuiInputTextFlags extra_flags
)
11390 char format
[16] = "%f";
11391 if (decimal_precision
>= 0)
11392 ImFormatString(format
, IM_ARRAYSIZE(format
), "%%.%df", decimal_precision
);
11393 return InputScalarN(label
, ImGuiDataType_Float
, v
, 2, NULL
, NULL
, format
, extra_flags
);
11396 bool ImGui::InputFloat3(const char* label
, float v
[3], int decimal_precision
, ImGuiInputTextFlags extra_flags
)
11398 char format
[16] = "%f";
11399 if (decimal_precision
>= 0)
11400 ImFormatString(format
, IM_ARRAYSIZE(format
), "%%.%df", decimal_precision
);
11401 return InputScalarN(label
, ImGuiDataType_Float
, v
, 3, NULL
, NULL
, format
, extra_flags
);
11404 bool ImGui::InputFloat4(const char* label
, float v
[4], int decimal_precision
, ImGuiInputTextFlags extra_flags
)
11406 char format
[16] = "%f";
11407 if (decimal_precision
>= 0)
11408 ImFormatString(format
, IM_ARRAYSIZE(format
), "%%.%df", decimal_precision
);
11409 return InputScalarN(label
, ImGuiDataType_Float
, v
, 4, NULL
, NULL
, format
, extra_flags
);
11411 #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
11413 bool ImGui::InputInt2(const char* label
, int v
[2], ImGuiInputTextFlags extra_flags
)
11415 return InputScalarN(label
, ImGuiDataType_S32
, v
, 2, NULL
, NULL
, "%d", extra_flags
);
11418 bool ImGui::InputInt3(const char* label
, int v
[3], ImGuiInputTextFlags extra_flags
)
11420 return InputScalarN(label
, ImGuiDataType_S32
, v
, 3, NULL
, NULL
, "%d", extra_flags
);
11423 bool ImGui::InputInt4(const char* label
, int v
[4], ImGuiInputTextFlags extra_flags
)
11425 return InputScalarN(label
, ImGuiDataType_S32
, v
, 4, NULL
, NULL
, "%d", extra_flags
);
11428 static float CalcMaxPopupHeightFromItemCount(int items_count
)
11430 ImGuiContext
& g
= *GImGui
;
11431 if (items_count
<= 0)
11433 return (g
.FontSize
+ g
.Style
.ItemSpacing
.y
) * items_count
- g
.Style
.ItemSpacing
.y
+ (g
.Style
.WindowPadding
.y
* 2);
11436 bool ImGui::BeginCombo(const char* label
, const char* preview_value
, ImGuiComboFlags flags
)
11438 // Always consume the SetNextWindowSizeConstraint() call in our early return paths
11439 ImGuiContext
& g
= *GImGui
;
11440 ImGuiCond backup_next_window_size_constraint
= g
.NextWindowData
.SizeConstraintCond
;
11441 g
.NextWindowData
.SizeConstraintCond
= 0;
11443 ImGuiWindow
* window
= GetCurrentWindow();
11444 if (window
->SkipItems
)
11447 IM_ASSERT((flags
& (ImGuiComboFlags_NoArrowButton
| ImGuiComboFlags_NoPreview
)) != (ImGuiComboFlags_NoArrowButton
| ImGuiComboFlags_NoPreview
)); // Can't use both flags together
11449 const ImGuiStyle
& style
= g
.Style
;
11450 const ImGuiID id
= window
->GetID(label
);
11452 const float arrow_size
= (flags
& ImGuiComboFlags_NoArrowButton
) ? 0.0f
: GetFrameHeight();
11453 const ImVec2 label_size
= CalcTextSize(label
, NULL
, true);
11454 const float w
= (flags
& ImGuiComboFlags_NoPreview
) ? arrow_size
: CalcItemWidth();
11455 const ImRect
frame_bb(window
->DC
.CursorPos
, window
->DC
.CursorPos
+ ImVec2(w
, label_size
.y
+ style
.FramePadding
.y
*2.0f
));
11456 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
));
11457 ItemSize(total_bb
, style
.FramePadding
.y
);
11458 if (!ItemAdd(total_bb
, id
, &frame_bb
))
11461 bool hovered
, held
;
11462 bool pressed
= ButtonBehavior(frame_bb
, id
, &hovered
, &held
);
11463 bool popup_open
= IsPopupOpen(id
);
11465 const ImRect
value_bb(frame_bb
.Min
, frame_bb
.Max
- ImVec2(arrow_size
, 0.0f
));
11466 const ImU32 frame_col
= GetColorU32(hovered
? ImGuiCol_FrameBgHovered
: ImGuiCol_FrameBg
);
11467 RenderNavHighlight(frame_bb
, id
);
11468 if (!(flags
& ImGuiComboFlags_NoPreview
))
11469 window
->DrawList
->AddRectFilled(frame_bb
.Min
, ImVec2(frame_bb
.Max
.x
- arrow_size
, frame_bb
.Max
.y
), frame_col
, style
.FrameRounding
, ImDrawCornerFlags_Left
);
11470 if (!(flags
& ImGuiComboFlags_NoArrowButton
))
11472 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
);
11473 RenderArrow(ImVec2(frame_bb
.Max
.x
- arrow_size
+ style
.FramePadding
.y
, frame_bb
.Min
.y
+ style
.FramePadding
.y
), ImGuiDir_Down
);
11475 RenderFrameBorder(frame_bb
.Min
, frame_bb
.Max
, style
.FrameRounding
);
11476 if (preview_value
!= NULL
&& !(flags
& ImGuiComboFlags_NoPreview
))
11477 RenderTextClipped(frame_bb
.Min
+ style
.FramePadding
, value_bb
.Max
, preview_value
, NULL
, NULL
, ImVec2(0.0f
,0.0f
));
11478 if (label_size
.x
> 0)
11479 RenderText(ImVec2(frame_bb
.Max
.x
+ style
.ItemInnerSpacing
.x
, frame_bb
.Min
.y
+ style
.FramePadding
.y
), label
);
11481 if ((pressed
|| g
.NavActivateId
== id
) && !popup_open
)
11483 if (window
->DC
.NavLayerCurrent
== 0)
11484 window
->NavLastIds
[0] = id
;
11492 if (backup_next_window_size_constraint
)
11494 g
.NextWindowData
.SizeConstraintCond
= backup_next_window_size_constraint
;
11495 g
.NextWindowData
.SizeConstraintRect
.Min
.x
= ImMax(g
.NextWindowData
.SizeConstraintRect
.Min
.x
, w
);
11499 if ((flags
& ImGuiComboFlags_HeightMask_
) == 0)
11500 flags
|= ImGuiComboFlags_HeightRegular
;
11501 IM_ASSERT(ImIsPowerOfTwo(flags
& ImGuiComboFlags_HeightMask_
)); // Only one
11502 int popup_max_height_in_items
= -1;
11503 if (flags
& ImGuiComboFlags_HeightRegular
) popup_max_height_in_items
= 8;
11504 else if (flags
& ImGuiComboFlags_HeightSmall
) popup_max_height_in_items
= 4;
11505 else if (flags
& ImGuiComboFlags_HeightLarge
) popup_max_height_in_items
= 20;
11506 SetNextWindowSizeConstraints(ImVec2(w
, 0.0f
), ImVec2(FLT_MAX
, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items
)));
11510 ImFormatString(name
, IM_ARRAYSIZE(name
), "##Combo_%02d", g
.CurrentPopupStack
.Size
); // Recycle windows based on depth
11512 // Peak into expected window size so we can position it
11513 if (ImGuiWindow
* popup_window
= FindWindowByName(name
))
11514 if (popup_window
->WasActive
)
11516 ImVec2 size_contents
= CalcSizeContents(popup_window
);
11517 ImVec2 size_expected
= CalcSizeAfterConstraint(popup_window
, CalcSizeAutoFit(popup_window
, size_contents
));
11518 if (flags
& ImGuiComboFlags_PopupAlignLeft
)
11519 popup_window
->AutoPosLastDirection
= ImGuiDir_Left
;
11520 ImRect r_outer
= FindAllowedExtentRectForWindow(popup_window
);
11521 ImVec2 pos
= FindBestWindowPosForPopupEx(frame_bb
.GetBL(), size_expected
, &popup_window
->AutoPosLastDirection
, r_outer
, frame_bb
, ImGuiPopupPositionPolicy_ComboBox
);
11522 SetNextWindowPos(pos
);
11525 // Horizontally align ourselves with the framed text
11526 ImGuiWindowFlags window_flags
= ImGuiWindowFlags_AlwaysAutoResize
| ImGuiWindowFlags_Popup
| ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoSavedSettings
;
11527 PushStyleVar(ImGuiStyleVar_WindowPadding
, ImVec2(style
.FramePadding
.x
, style
.WindowPadding
.y
));
11528 bool ret
= Begin(name
, NULL
, window_flags
);
11533 IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above
11539 void ImGui::EndCombo()
11544 // Getter for the old Combo() API: const char*[]
11545 static bool Items_ArrayGetter(void* data
, int idx
, const char** out_text
)
11547 const char* const* items
= (const char* const*)data
;
11549 *out_text
= items
[idx
];
11553 // Getter for the old Combo() API: "item1\0item2\0item3\0"
11554 static bool Items_SingleStringGetter(void* data
, int idx
, const char** out_text
)
11556 // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
11557 const char* items_separated_by_zeros
= (const char*)data
;
11558 int items_count
= 0;
11559 const char* p
= items_separated_by_zeros
;
11562 if (idx
== items_count
)
11564 p
+= strlen(p
) + 1;
11574 // Old API, prefer using BeginCombo() nowadays if you can.
11575 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
)
11577 ImGuiContext
& g
= *GImGui
;
11579 // Call the getter to obtain the preview string which is a parameter to BeginCombo()
11580 const char* preview_value
= NULL
;
11581 if (*current_item
>= 0 && *current_item
< items_count
)
11582 items_getter(data
, *current_item
, &preview_value
);
11584 // The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here.
11585 if (popup_max_height_in_items
!= -1 && !g
.NextWindowData
.SizeConstraintCond
)
11586 SetNextWindowSizeConstraints(ImVec2(0,0), ImVec2(FLT_MAX
, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items
)));
11588 if (!BeginCombo(label
, preview_value
, ImGuiComboFlags_None
))
11592 // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed)
11593 bool value_changed
= false;
11594 for (int i
= 0; i
< items_count
; i
++)
11596 PushID((void*)(intptr_t)i
);
11597 const bool item_selected
= (i
== *current_item
);
11598 const char* item_text
;
11599 if (!items_getter(data
, i
, &item_text
))
11600 item_text
= "*Unknown item*";
11601 if (Selectable(item_text
, item_selected
))
11603 value_changed
= true;
11607 SetItemDefaultFocus();
11612 return value_changed
;
11615 // Combo box helper allowing to pass an array of strings.
11616 bool ImGui::Combo(const char* label
, int* current_item
, const char* const items
[], int items_count
, int height_in_items
)
11618 const bool value_changed
= Combo(label
, current_item
, Items_ArrayGetter
, (void*)items
, items_count
, height_in_items
);
11619 return value_changed
;
11622 // Combo box helper allowing to pass all items in a single string literal holding multiple zero-terminated items "item1\0item2\0"
11623 bool ImGui::Combo(const char* label
, int* current_item
, const char* items_separated_by_zeros
, int height_in_items
)
11625 int items_count
= 0;
11626 const char* p
= items_separated_by_zeros
; // FIXME-OPT: Avoid computing this, or at least only when combo is open
11629 p
+= strlen(p
) + 1;
11632 bool value_changed
= Combo(label
, current_item
, Items_SingleStringGetter
, (void*)items_separated_by_zeros
, items_count
, height_in_items
);
11633 return value_changed
;
11636 // Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image.
11637 // But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id.
11638 bool ImGui::Selectable(const char* label
, bool selected
, ImGuiSelectableFlags flags
, const ImVec2
& size_arg
)
11640 ImGuiWindow
* window
= GetCurrentWindow();
11641 if (window
->SkipItems
)
11644 ImGuiContext
& g
= *GImGui
;
11645 const ImGuiStyle
& style
= g
.Style
;
11647 if ((flags
& ImGuiSelectableFlags_SpanAllColumns
) && window
->DC
.ColumnsSet
) // FIXME-OPT: Avoid if vertically clipped.
11650 ImGuiID id
= window
->GetID(label
);
11651 ImVec2 label_size
= CalcTextSize(label
, NULL
, true);
11652 ImVec2
size(size_arg
.x
!= 0.0f
? size_arg
.x
: label_size
.x
, size_arg
.y
!= 0.0f
? size_arg
.y
: label_size
.y
);
11653 ImVec2 pos
= window
->DC
.CursorPos
;
11654 pos
.y
+= window
->DC
.CurrentLineTextBaseOffset
;
11655 ImRect
bb_inner(pos
, pos
+ size
);
11656 ItemSize(bb_inner
);
11658 // Fill horizontal space.
11659 ImVec2 window_padding
= window
->WindowPadding
;
11660 float max_x
= (flags
& ImGuiSelectableFlags_SpanAllColumns
) ? GetWindowContentRegionMax().x
: GetContentRegionMax().x
;
11661 float w_draw
= ImMax(label_size
.x
, window
->Pos
.x
+ max_x
- window_padding
.x
- window
->DC
.CursorPos
.x
);
11662 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
);
11663 ImRect
bb(pos
, pos
+ size_draw
);
11664 if (size_arg
.x
== 0.0f
|| (flags
& ImGuiSelectableFlags_DrawFillAvailWidth
))
11665 bb
.Max
.x
+= window_padding
.x
;
11667 // Selectables are tightly packed together, we extend the box to cover spacing between selectable.
11668 float spacing_L
= (float)(int)(style
.ItemSpacing
.x
* 0.5f
);
11669 float spacing_U
= (float)(int)(style
.ItemSpacing
.y
* 0.5f
);
11670 float spacing_R
= style
.ItemSpacing
.x
- spacing_L
;
11671 float spacing_D
= style
.ItemSpacing
.y
- spacing_U
;
11672 bb
.Min
.x
-= spacing_L
;
11673 bb
.Min
.y
-= spacing_U
;
11674 bb
.Max
.x
+= spacing_R
;
11675 bb
.Max
.y
+= spacing_D
;
11676 if (!ItemAdd(bb
, (flags
& ImGuiSelectableFlags_Disabled
) ? 0 : id
))
11678 if ((flags
& ImGuiSelectableFlags_SpanAllColumns
) && window
->DC
.ColumnsSet
)
11679 PushColumnClipRect();
11683 // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
11684 ImGuiButtonFlags button_flags
= 0;
11685 if (flags
& ImGuiSelectableFlags_NoHoldingActiveID
) button_flags
|= ImGuiButtonFlags_NoHoldingActiveID
;
11686 if (flags
& ImGuiSelectableFlags_PressedOnClick
) button_flags
|= ImGuiButtonFlags_PressedOnClick
;
11687 if (flags
& ImGuiSelectableFlags_PressedOnRelease
) button_flags
|= ImGuiButtonFlags_PressedOnRelease
;
11688 if (flags
& ImGuiSelectableFlags_Disabled
) button_flags
|= ImGuiButtonFlags_Disabled
;
11689 if (flags
& ImGuiSelectableFlags_AllowDoubleClick
) button_flags
|= ImGuiButtonFlags_PressedOnClickRelease
| ImGuiButtonFlags_PressedOnDoubleClick
;
11690 bool hovered
, held
;
11691 bool pressed
= ButtonBehavior(bb
, id
, &hovered
, &held
, button_flags
);
11692 if (flags
& ImGuiSelectableFlags_Disabled
)
11695 // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets)
11696 if (pressed
|| hovered
)
11697 if (!g
.NavDisableMouseHover
&& g
.NavWindow
== window
&& g
.NavLayer
== window
->DC
.NavLayerCurrent
)
11699 g
.NavDisableHighlight
= true;
11700 SetNavID(id
, window
->DC
.NavLayerCurrent
);
11703 MarkItemValueChanged(id
);
11706 if (hovered
|| selected
)
11708 const ImU32 col
= GetColorU32((held
&& hovered
) ? ImGuiCol_HeaderActive
: hovered
? ImGuiCol_HeaderHovered
: ImGuiCol_Header
);
11709 RenderFrame(bb
.Min
, bb
.Max
, col
, false, 0.0f
);
11710 RenderNavHighlight(bb
, id
, ImGuiNavHighlightFlags_TypeThin
| ImGuiNavHighlightFlags_NoRounding
);
11713 if ((flags
& ImGuiSelectableFlags_SpanAllColumns
) && window
->DC
.ColumnsSet
)
11715 PushColumnClipRect();
11716 bb
.Max
.x
-= (GetContentRegionMax().x
- max_x
);
11719 if (flags
& ImGuiSelectableFlags_Disabled
) PushStyleColor(ImGuiCol_Text
, g
.Style
.Colors
[ImGuiCol_TextDisabled
]);
11720 RenderTextClipped(bb_inner
.Min
, bb
.Max
, label
, NULL
, &label_size
, ImVec2(0.0f
,0.0f
));
11721 if (flags
& ImGuiSelectableFlags_Disabled
) PopStyleColor();
11723 // Automatically close popups
11724 if (pressed
&& (window
->Flags
& ImGuiWindowFlags_Popup
) && !(flags
& ImGuiSelectableFlags_DontClosePopups
) && !(window
->DC
.ItemFlags
& ImGuiItemFlags_SelectableDontClosePopup
))
11725 CloseCurrentPopup();
11729 bool ImGui::Selectable(const char* label
, bool* p_selected
, ImGuiSelectableFlags flags
, const ImVec2
& size_arg
)
11731 if (Selectable(label
, *p_selected
, flags
, size_arg
))
11733 *p_selected
= !*p_selected
;
11739 // FIXME: Rename to BeginListBox()
11740 // Helper to calculate the size of a listbox and display a label on the right.
11741 // Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty"
11742 bool ImGui::ListBoxHeader(const char* label
, const ImVec2
& size_arg
)
11744 ImGuiWindow
* window
= GetCurrentWindow();
11745 if (window
->SkipItems
)
11748 const ImGuiStyle
& style
= GetStyle();
11749 const ImGuiID id
= GetID(label
);
11750 const ImVec2 label_size
= CalcTextSize(label
, NULL
, true);
11752 // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
11753 ImVec2 size
= CalcItemSize(size_arg
, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f
+ style
.ItemSpacing
.y
);
11754 ImVec2 frame_size
= ImVec2(size
.x
, ImMax(size
.y
, label_size
.y
));
11755 ImRect
frame_bb(window
->DC
.CursorPos
, window
->DC
.CursorPos
+ frame_size
);
11756 ImRect
bb(frame_bb
.Min
, frame_bb
.Max
+ ImVec2(label_size
.x
> 0.0f
? style
.ItemInnerSpacing
.x
+ label_size
.x
: 0.0f
, 0.0f
));
11757 window
->DC
.LastItemRect
= bb
; // Forward storage for ListBoxFooter.. dodgy.
11760 if (label_size
.x
> 0)
11761 RenderText(ImVec2(frame_bb
.Max
.x
+ style
.ItemInnerSpacing
.x
, frame_bb
.Min
.y
+ style
.FramePadding
.y
), label
);
11763 BeginChildFrame(id
, frame_bb
.GetSize());
11767 // FIXME: Rename to BeginListBox()
11768 bool ImGui::ListBoxHeader(const char* label
, int items_count
, int height_in_items
)
11770 // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
11771 // 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.
11772 // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution.
11773 if (height_in_items
< 0)
11774 height_in_items
= ImMin(items_count
, 7);
11775 float height_in_items_f
= height_in_items
< items_count
? (height_in_items
+ 0.40f
) : (height_in_items
+ 0.00f
);
11777 // 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().
11780 size
.y
= GetTextLineHeightWithSpacing() * height_in_items_f
+ GetStyle().ItemSpacing
.y
;
11781 return ListBoxHeader(label
, size
);
11784 // FIXME: Rename to EndListBox()
11785 void ImGui::ListBoxFooter()
11787 ImGuiWindow
* parent_window
= GetCurrentWindow()->ParentWindow
;
11788 const ImRect bb
= parent_window
->DC
.LastItemRect
;
11789 const ImGuiStyle
& style
= GetStyle();
11793 // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect)
11794 // We call SameLine() to restore DC.CurrentLine* data
11796 parent_window
->DC
.CursorPos
= bb
.Min
;
11797 ItemSize(bb
, style
.FramePadding
.y
);
11801 bool ImGui::ListBox(const char* label
, int* current_item
, const char* const items
[], int items_count
, int height_items
)
11803 const bool value_changed
= ListBox(label
, current_item
, Items_ArrayGetter
, (void*)items
, items_count
, height_items
);
11804 return value_changed
;
11807 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
)
11809 if (!ListBoxHeader(label
, items_count
, height_in_items
))
11812 // 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.
11813 bool value_changed
= false;
11814 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.
11815 while (clipper
.Step())
11816 for (int i
= clipper
.DisplayStart
; i
< clipper
.DisplayEnd
; i
++)
11818 const bool item_selected
= (i
== *current_item
);
11819 const char* item_text
;
11820 if (!items_getter(data
, i
, &item_text
))
11821 item_text
= "*Unknown item*";
11824 if (Selectable(item_text
, item_selected
))
11827 value_changed
= true;
11830 SetItemDefaultFocus();
11834 return value_changed
;
11837 bool ImGui::MenuItem(const char* label
, const char* shortcut
, bool selected
, bool enabled
)
11839 ImGuiWindow
* window
= GetCurrentWindow();
11840 if (window
->SkipItems
)
11843 ImGuiContext
& g
= *GImGui
;
11844 ImGuiStyle
& style
= g
.Style
;
11845 ImVec2 pos
= window
->DC
.CursorPos
;
11846 ImVec2 label_size
= CalcTextSize(label
, NULL
, true);
11848 ImGuiSelectableFlags flags
= ImGuiSelectableFlags_PressedOnRelease
| (enabled
? 0 : ImGuiSelectableFlags_Disabled
);
11850 if (window
->DC
.LayoutType
== ImGuiLayoutType_Horizontal
)
11852 // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
11853 // Note that in this situation we render neither the shortcut neither the selected tick mark
11854 float w
= label_size
.x
;
11855 window
->DC
.CursorPos
.x
+= (float)(int)(style
.ItemSpacing
.x
* 0.5f
);
11856 PushStyleVar(ImGuiStyleVar_ItemSpacing
, style
.ItemSpacing
* 2.0f
);
11857 pressed
= Selectable(label
, false, flags
, ImVec2(w
, 0.0f
));
11859 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().
11863 ImVec2 shortcut_size
= shortcut
? CalcTextSize(shortcut
, NULL
) : ImVec2(0.0f
, 0.0f
);
11864 float w
= window
->MenuColumns
.DeclColumns(label_size
.x
, shortcut_size
.x
, (float)(int)(g
.FontSize
* 1.20f
)); // Feedback for next frame
11865 float extra_w
= ImMax(0.0f
, GetContentRegionAvail().x
- w
);
11866 pressed
= Selectable(label
, false, flags
| ImGuiSelectableFlags_DrawFillAvailWidth
, ImVec2(w
, 0.0f
));
11867 if (shortcut_size
.x
> 0.0f
)
11869 PushStyleColor(ImGuiCol_Text
, g
.Style
.Colors
[ImGuiCol_TextDisabled
]);
11870 RenderText(pos
+ ImVec2(window
->MenuColumns
.Pos
[1] + extra_w
, 0.0f
), shortcut
, NULL
, false);
11874 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
);
11879 bool ImGui::MenuItem(const char* label
, const char* shortcut
, bool* p_selected
, bool enabled
)
11881 if (MenuItem(label
, shortcut
, p_selected
? *p_selected
: false, enabled
))
11884 *p_selected
= !*p_selected
;
11890 // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.
11891 bool ImGui::BeginMainMenuBar()
11893 ImGuiContext
& g
= *GImGui
;
11894 g
.NextWindowData
.MenuBarOffsetMinVal
= ImVec2(g
.Style
.DisplaySafeAreaPadding
.x
, ImMax(g
.Style
.DisplaySafeAreaPadding
.y
- g
.Style
.FramePadding
.y
, 0.0f
));
11895 SetNextWindowPos(ImVec2(0.0f
, 0.0f
));
11896 SetNextWindowSize(ImVec2(g
.IO
.DisplaySize
.x
, g
.NextWindowData
.MenuBarOffsetMinVal
.y
+ g
.FontBaseSize
+ g
.Style
.FramePadding
.y
));
11897 PushStyleVar(ImGuiStyleVar_WindowRounding
, 0.0f
);
11898 PushStyleVar(ImGuiStyleVar_WindowMinSize
, ImVec2(0,0));
11899 ImGuiWindowFlags window_flags
= ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoScrollbar
| ImGuiWindowFlags_NoSavedSettings
| ImGuiWindowFlags_MenuBar
;
11900 bool is_open
= Begin("##MainMenuBar", NULL
, window_flags
) && BeginMenuBar();
11902 g
.NextWindowData
.MenuBarOffsetMinVal
= ImVec2(0.0f
, 0.0f
);
11911 void ImGui::EndMainMenuBar()
11915 // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window
11916 ImGuiContext
& g
= *GImGui
;
11917 if (g
.CurrentWindow
== g
.NavWindow
&& g
.NavLayer
== 0)
11918 FocusFrontMostActiveWindow(g
.NavWindow
);
11923 bool ImGui::BeginMenuBar()
11925 ImGuiWindow
* window
= GetCurrentWindow();
11926 if (window
->SkipItems
)
11928 if (!(window
->Flags
& ImGuiWindowFlags_MenuBar
))
11931 IM_ASSERT(!window
->DC
.MenuBarAppending
);
11932 BeginGroup(); // Backup position on layer 0
11933 PushID("##menubar");
11935 // 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.
11936 // 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.
11937 ImRect bar_rect
= window
->MenuBarRect();
11938 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
));
11939 clip_rect
.ClipWith(window
->OuterRectClipped
);
11940 PushClipRect(clip_rect
.Min
, clip_rect
.Max
, false);
11942 window
->DC
.CursorPos
= ImVec2(bar_rect
.Min
.x
+ window
->DC
.MenuBarOffset
.x
, bar_rect
.Min
.y
+ window
->DC
.MenuBarOffset
.y
);
11943 window
->DC
.LayoutType
= ImGuiLayoutType_Horizontal
;
11944 window
->DC
.NavLayerCurrent
++;
11945 window
->DC
.NavLayerCurrentMask
<<= 1;
11946 window
->DC
.MenuBarAppending
= true;
11947 AlignTextToFramePadding();
11951 void ImGui::EndMenuBar()
11953 ImGuiWindow
* window
= GetCurrentWindow();
11954 if (window
->SkipItems
)
11956 ImGuiContext
& g
= *GImGui
;
11958 // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings.
11959 if (NavMoveRequestButNoResultYet() && (g
.NavMoveDir
== ImGuiDir_Left
|| g
.NavMoveDir
== ImGuiDir_Right
) && (g
.NavWindow
->Flags
& ImGuiWindowFlags_ChildMenu
))
11961 ImGuiWindow
* nav_earliest_child
= g
.NavWindow
;
11962 while (nav_earliest_child
->ParentWindow
&& (nav_earliest_child
->ParentWindow
->Flags
& ImGuiWindowFlags_ChildMenu
))
11963 nav_earliest_child
= nav_earliest_child
->ParentWindow
;
11964 if (nav_earliest_child
->ParentWindow
== window
&& nav_earliest_child
->DC
.ParentLayoutType
== ImGuiLayoutType_Horizontal
&& g
.NavMoveRequestForward
== ImGuiNavForward_None
)
11966 // To do so we claim focus back, restore NavId and then process the movement request for yet another frame.
11967 // 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)
11968 IM_ASSERT(window
->DC
.NavLayerActiveMaskNext
& 0x02); // Sanity check
11969 FocusWindow(window
);
11970 SetNavIDWithRectRel(window
->NavLastIds
[1], 1, window
->NavRectRel
[1]);
11972 g
.NavDisableHighlight
= true; // Hide highlight for the current frame so we don't see the intermediary selection.
11973 g
.NavMoveRequestForward
= ImGuiNavForward_ForwardQueued
;
11974 NavMoveRequestCancel();
11978 IM_ASSERT(window
->Flags
& ImGuiWindowFlags_MenuBar
);
11979 IM_ASSERT(window
->DC
.MenuBarAppending
);
11982 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.
11983 window
->DC
.GroupStack
.back().AdvanceCursor
= false;
11984 EndGroup(); // Restore position on layer 0
11985 window
->DC
.LayoutType
= ImGuiLayoutType_Vertical
;
11986 window
->DC
.NavLayerCurrent
--;
11987 window
->DC
.NavLayerCurrentMask
>>= 1;
11988 window
->DC
.MenuBarAppending
= false;
11991 bool ImGui::BeginMenu(const char* label
, bool enabled
)
11993 ImGuiWindow
* window
= GetCurrentWindow();
11994 if (window
->SkipItems
)
11997 ImGuiContext
& g
= *GImGui
;
11998 const ImGuiStyle
& style
= g
.Style
;
11999 const ImGuiID id
= window
->GetID(label
);
12001 ImVec2 label_size
= CalcTextSize(label
, NULL
, true);
12004 bool menu_is_open
= IsPopupOpen(id
);
12005 bool menuset_is_open
= !(window
->Flags
& ImGuiWindowFlags_Popup
) && (g
.OpenPopupStack
.Size
> g
.CurrentPopupStack
.Size
&& g
.OpenPopupStack
[g
.CurrentPopupStack
.Size
].OpenParentId
== window
->IDStack
.back());
12006 ImGuiWindow
* backed_nav_window
= g
.NavWindow
;
12007 if (menuset_is_open
)
12008 g
.NavWindow
= window
; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent)
12010 // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestWindowPosForPopup).
12011 ImVec2 popup_pos
, pos
= window
->DC
.CursorPos
;
12012 if (window
->DC
.LayoutType
== ImGuiLayoutType_Horizontal
)
12014 // Menu inside an horizontal menu bar
12015 // Selectable extend their highlight by half ItemSpacing in each direction.
12016 // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin()
12017 popup_pos
= ImVec2(pos
.x
- window
->WindowPadding
.x
, pos
.y
- style
.FramePadding
.y
+ window
->MenuBarHeight());
12018 window
->DC
.CursorPos
.x
+= (float)(int)(style
.ItemSpacing
.x
* 0.5f
);
12019 PushStyleVar(ImGuiStyleVar_ItemSpacing
, style
.ItemSpacing
* 2.0f
);
12020 float w
= label_size
.x
;
12021 pressed
= Selectable(label
, menu_is_open
, ImGuiSelectableFlags_NoHoldingActiveID
| ImGuiSelectableFlags_PressedOnClick
| ImGuiSelectableFlags_DontClosePopups
| (!enabled
? ImGuiSelectableFlags_Disabled
: 0), ImVec2(w
, 0.0f
));
12023 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().
12027 // Menu inside a menu
12028 popup_pos
= ImVec2(pos
.x
, pos
.y
- style
.WindowPadding
.y
);
12029 float w
= window
->MenuColumns
.DeclColumns(label_size
.x
, 0.0f
, (float)(int)(g
.FontSize
* 1.20f
)); // Feedback to next frame
12030 float extra_w
= ImMax(0.0f
, GetContentRegionAvail().x
- w
);
12031 pressed
= Selectable(label
, menu_is_open
, ImGuiSelectableFlags_NoHoldingActiveID
| ImGuiSelectableFlags_PressedOnClick
| ImGuiSelectableFlags_DontClosePopups
| ImGuiSelectableFlags_DrawFillAvailWidth
| (!enabled
? ImGuiSelectableFlags_Disabled
: 0), ImVec2(w
, 0.0f
));
12032 if (!enabled
) PushStyleColor(ImGuiCol_Text
, g
.Style
.Colors
[ImGuiCol_TextDisabled
]);
12033 RenderArrow(pos
+ ImVec2(window
->MenuColumns
.Pos
[2] + extra_w
+ g
.FontSize
* 0.30f
, 0.0f
), ImGuiDir_Right
);
12034 if (!enabled
) PopStyleColor();
12037 const bool hovered
= enabled
&& ItemHoverable(window
->DC
.LastItemRect
, id
);
12038 if (menuset_is_open
)
12039 g
.NavWindow
= backed_nav_window
;
12041 bool want_open
= false, want_close
= false;
12042 if (window
->DC
.LayoutType
== ImGuiLayoutType_Vertical
) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
12044 // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
12045 bool moving_within_opened_triangle
= false;
12046 if (g
.HoveredWindow
== window
&& g
.OpenPopupStack
.Size
> g
.CurrentPopupStack
.Size
&& g
.OpenPopupStack
[g
.CurrentPopupStack
.Size
].ParentWindow
== window
&& !(window
->Flags
& ImGuiWindowFlags_MenuBar
))
12048 if (ImGuiWindow
* next_window
= g
.OpenPopupStack
[g
.CurrentPopupStack
.Size
].Window
)
12050 ImRect next_window_rect
= next_window
->Rect();
12051 ImVec2 ta
= g
.IO
.MousePos
- g
.IO
.MouseDelta
;
12052 ImVec2 tb
= (window
->Pos
.x
< next_window
->Pos
.x
) ? next_window_rect
.GetTL() : next_window_rect
.GetTR();
12053 ImVec2 tc
= (window
->Pos
.x
< next_window
->Pos
.x
) ? next_window_rect
.GetBL() : next_window_rect
.GetBR();
12054 float extra
= ImClamp(ImFabs(ta
.x
- tb
.x
) * 0.30f
, 5.0f
, 30.0f
); // add a bit of extra slack.
12055 ta
.x
+= (window
->Pos
.x
< next_window
->Pos
.x
) ? -0.5f
: +0.5f
; // to avoid numerical issues
12056 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?
12057 tc
.y
= ta
.y
+ ImMin((tc
.y
+ extra
) - ta
.y
, +100.0f
);
12058 moving_within_opened_triangle
= ImTriangleContainsPoint(ta
, tb
, tc
, g
.IO
.MousePos
);
12059 //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
12063 want_close
= (menu_is_open
&& !hovered
&& g
.HoveredWindow
== window
&& g
.HoveredIdPreviousFrame
!= 0 && g
.HoveredIdPreviousFrame
!= id
&& !moving_within_opened_triangle
);
12064 want_open
= (!menu_is_open
&& hovered
&& !moving_within_opened_triangle
) || (!menu_is_open
&& hovered
&& pressed
);
12066 if (g
.NavActivateId
== id
)
12068 want_close
= menu_is_open
;
12069 want_open
= !menu_is_open
;
12071 if (g
.NavId
== id
&& g
.NavMoveRequest
&& g
.NavMoveDir
== ImGuiDir_Right
) // Nav-Right to open
12074 NavMoveRequestCancel();
12080 if (menu_is_open
&& pressed
&& menuset_is_open
) // Click an open menu again to close it
12083 want_open
= menu_is_open
= false;
12085 else if (pressed
|| (hovered
&& menuset_is_open
&& !menu_is_open
)) // First click to open, then hover to open others
12089 else if (g
.NavId
== id
&& g
.NavMoveRequest
&& g
.NavMoveDir
== ImGuiDir_Down
) // Nav-Down to open
12092 NavMoveRequestCancel();
12096 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.. }'
12098 if (want_close
&& IsPopupOpen(id
))
12099 ClosePopupToLevel(g
.CurrentPopupStack
.Size
);
12101 if (!menu_is_open
&& want_open
&& g
.OpenPopupStack
.Size
> g
.CurrentPopupStack
.Size
)
12103 // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
12108 menu_is_open
|= want_open
;
12114 // 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)
12115 SetNextWindowPos(popup_pos
, ImGuiCond_Always
);
12116 ImGuiWindowFlags flags
= ImGuiWindowFlags_AlwaysAutoResize
| ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoSavedSettings
| ((window
->Flags
& (ImGuiWindowFlags_Popup
|ImGuiWindowFlags_ChildMenu
)) ? ImGuiWindowFlags_ChildMenu
|ImGuiWindowFlags_ChildWindow
: ImGuiWindowFlags_ChildMenu
);
12117 menu_is_open
= BeginPopupEx(id
, flags
); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
12120 return menu_is_open
;
12123 void ImGui::EndMenu()
12125 // Nav: When a left move request _within our child menu_ failed, close the menu.
12126 // A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs.
12127 // 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.
12128 ImGuiContext
& g
= *GImGui
;
12129 ImGuiWindow
* window
= g
.CurrentWindow
;
12130 if (g
.NavWindow
&& g
.NavWindow
->ParentWindow
== window
&& g
.NavMoveDir
== ImGuiDir_Left
&& NavMoveRequestButNoResultYet() && window
->DC
.LayoutType
== ImGuiLayoutType_Vertical
)
12132 ClosePopupToLevel(g
.OpenPopupStack
.Size
- 1);
12133 NavMoveRequestCancel();
12139 // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
12140 void ImGui::ColorTooltip(const char* text
, const float* col
, ImGuiColorEditFlags flags
)
12142 ImGuiContext
& g
= *GImGui
;
12144 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]);
12145 BeginTooltipEx(0, true);
12147 const char* text_end
= text
? FindRenderedTextEnd(text
, NULL
) : text
;
12148 if (text_end
> text
)
12150 TextUnformatted(text
, text_end
);
12154 ImVec2
sz(g
.FontSize
* 3 + g
.Style
.FramePadding
.y
* 2, g
.FontSize
* 3 + g
.Style
.FramePadding
.y
* 2);
12155 ColorButton("##preview", ImVec4(col
[0], col
[1], col
[2], col
[3]), (flags
& (ImGuiColorEditFlags_NoAlpha
| ImGuiColorEditFlags_AlphaPreview
| ImGuiColorEditFlags_AlphaPreviewHalf
)) | ImGuiColorEditFlags_NoTooltip
, sz
);
12157 if (flags
& ImGuiColorEditFlags_NoAlpha
)
12158 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]);
12160 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]);
12164 static inline ImU32
ImAlphaBlendColor(ImU32 col_a
, ImU32 col_b
)
12166 float t
= ((col_b
>> IM_COL32_A_SHIFT
) & 0xFF) / 255.f
;
12167 int r
= ImLerp((int)(col_a
>> IM_COL32_R_SHIFT
) & 0xFF, (int)(col_b
>> IM_COL32_R_SHIFT
) & 0xFF, t
);
12168 int g
= ImLerp((int)(col_a
>> IM_COL32_G_SHIFT
) & 0xFF, (int)(col_b
>> IM_COL32_G_SHIFT
) & 0xFF, t
);
12169 int b
= ImLerp((int)(col_a
>> IM_COL32_B_SHIFT
) & 0xFF, (int)(col_b
>> IM_COL32_B_SHIFT
) & 0xFF, t
);
12170 return IM_COL32(r
, g
, b
, 0xFF);
12173 // 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.
12174 // 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.
12175 void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min
, ImVec2 p_max
, ImU32 col
, float grid_step
, ImVec2 grid_off
, float rounding
, int rounding_corners_flags
)
12177 ImGuiWindow
* window
= GetCurrentWindow();
12178 if (((col
& IM_COL32_A_MASK
) >> IM_COL32_A_SHIFT
) < 0xFF)
12180 ImU32 col_bg1
= GetColorU32(ImAlphaBlendColor(IM_COL32(204,204,204,255), col
));
12181 ImU32 col_bg2
= GetColorU32(ImAlphaBlendColor(IM_COL32(128,128,128,255), col
));
12182 window
->DrawList
->AddRectFilled(p_min
, p_max
, col_bg1
, rounding
, rounding_corners_flags
);
12185 for (float y
= p_min
.y
+ grid_off
.y
; y
< p_max
.y
; y
+= grid_step
, yi
++)
12187 float y1
= ImClamp(y
, p_min
.y
, p_max
.y
), y2
= ImMin(y
+ grid_step
, p_max
.y
);
12190 for (float x
= p_min
.x
+ grid_off
.x
+ (yi
& 1) * grid_step
; x
< p_max
.x
; x
+= grid_step
* 2.0f
)
12192 float x1
= ImClamp(x
, p_min
.x
, p_max
.x
), x2
= ImMin(x
+ grid_step
, p_max
.x
);
12195 int rounding_corners_flags_cell
= 0;
12196 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
; }
12197 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
; }
12198 rounding_corners_flags_cell
&= rounding_corners_flags
;
12199 window
->DrawList
->AddRectFilled(ImVec2(x1
,y1
), ImVec2(x2
,y2
), col_bg2
, rounding_corners_flags_cell
? rounding
: 0.0f
, rounding_corners_flags_cell
);
12205 window
->DrawList
->AddRectFilled(p_min
, p_max
, col
, rounding
, rounding_corners_flags
);
12209 void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags
)
12211 ImGuiContext
& g
= *GImGui
;
12212 if ((flags
& ImGuiColorEditFlags__InputsMask
) == 0)
12213 flags
|= ImGuiColorEditFlags__OptionsDefault
& ImGuiColorEditFlags__InputsMask
;
12214 if ((flags
& ImGuiColorEditFlags__DataTypeMask
) == 0)
12215 flags
|= ImGuiColorEditFlags__OptionsDefault
& ImGuiColorEditFlags__DataTypeMask
;
12216 if ((flags
& ImGuiColorEditFlags__PickerMask
) == 0)
12217 flags
|= ImGuiColorEditFlags__OptionsDefault
& ImGuiColorEditFlags__PickerMask
;
12218 IM_ASSERT(ImIsPowerOfTwo((int)(flags
& ImGuiColorEditFlags__InputsMask
))); // Check only 1 option is selected
12219 IM_ASSERT(ImIsPowerOfTwo((int)(flags
& ImGuiColorEditFlags__DataTypeMask
))); // Check only 1 option is selected
12220 IM_ASSERT(ImIsPowerOfTwo((int)(flags
& ImGuiColorEditFlags__PickerMask
))); // Check only 1 option is selected
12221 g
.ColorEditOptions
= flags
;
12224 // A little colored square. Return true when clicked.
12225 // FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip.
12226 // 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip.
12227 bool ImGui::ColorButton(const char* desc_id
, const ImVec4
& col
, ImGuiColorEditFlags flags
, ImVec2 size
)
12229 ImGuiWindow
* window
= GetCurrentWindow();
12230 if (window
->SkipItems
)
12233 ImGuiContext
& g
= *GImGui
;
12234 const ImGuiID id
= window
->GetID(desc_id
);
12235 float default_size
= GetFrameHeight();
12236 if (size
.x
== 0.0f
)
12237 size
.x
= default_size
;
12238 if (size
.y
== 0.0f
)
12239 size
.y
= default_size
;
12240 const ImRect
bb(window
->DC
.CursorPos
, window
->DC
.CursorPos
+ size
);
12241 ItemSize(bb
, (size
.y
>= default_size
) ? g
.Style
.FramePadding
.y
: 0.0f
);
12242 if (!ItemAdd(bb
, id
))
12245 bool hovered
, held
;
12246 bool pressed
= ButtonBehavior(bb
, id
, &hovered
, &held
);
12248 if (flags
& ImGuiColorEditFlags_NoAlpha
)
12249 flags
&= ~(ImGuiColorEditFlags_AlphaPreview
| ImGuiColorEditFlags_AlphaPreviewHalf
);
12251 ImVec4
col_without_alpha(col
.x
, col
.y
, col
.z
, 1.0f
);
12252 float grid_step
= ImMin(size
.x
, size
.y
) / 2.99f
;
12253 float rounding
= ImMin(g
.Style
.FrameRounding
, grid_step
* 0.5f
);
12254 ImRect bb_inner
= bb
;
12255 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.
12256 bb_inner
.Expand(off
);
12257 if ((flags
& ImGuiColorEditFlags_AlphaPreviewHalf
) && col
.w
< 1.0f
)
12259 float mid_x
= (float)(int)((bb_inner
.Min
.x
+ bb_inner
.Max
.x
) * 0.5f
+ 0.5f
);
12260 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
);
12261 window
->DrawList
->AddRectFilled(bb_inner
.Min
, ImVec2(mid_x
, bb_inner
.Max
.y
), GetColorU32(col_without_alpha
), rounding
, ImDrawCornerFlags_TopLeft
|ImDrawCornerFlags_BotLeft
);
12265 // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha
12266 ImVec4 col_source
= (flags
& ImGuiColorEditFlags_AlphaPreview
) ? col
: col_without_alpha
;
12267 if (col_source
.w
< 1.0f
)
12268 RenderColorRectWithAlphaCheckerboard(bb_inner
.Min
, bb_inner
.Max
, GetColorU32(col_source
), grid_step
, ImVec2(off
, off
), rounding
);
12270 window
->DrawList
->AddRectFilled(bb_inner
.Min
, bb_inner
.Max
, GetColorU32(col_source
), rounding
, ImDrawCornerFlags_All
);
12272 RenderNavHighlight(bb
, id
);
12273 if (g
.Style
.FrameBorderSize
> 0.0f
)
12274 RenderFrameBorder(bb
.Min
, bb
.Max
, rounding
);
12276 window
->DrawList
->AddRect(bb
.Min
, bb
.Max
, GetColorU32(ImGuiCol_FrameBg
), rounding
); // Color button are often in need of some sort of border
12278 // Drag and Drop Source
12279 // NB: The ActiveId test is merely an optional micro-optimization, BeginDragDropSource() does the same test.
12280 if (g
.ActiveId
== id
&& !(flags
& ImGuiColorEditFlags_NoDragDrop
) && BeginDragDropSource())
12282 if (flags
& ImGuiColorEditFlags_NoAlpha
)
12283 SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F
, &col
, sizeof(float) * 3, ImGuiCond_Once
);
12285 SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F
, &col
, sizeof(float) * 4, ImGuiCond_Once
);
12286 ColorButton(desc_id
, col
, flags
);
12288 TextUnformatted("Color");
12289 EndDragDropSource();
12293 if (!(flags
& ImGuiColorEditFlags_NoTooltip
) && hovered
)
12294 ColorTooltip(desc_id
, &col
.x
, flags
& (ImGuiColorEditFlags_NoAlpha
| ImGuiColorEditFlags_AlphaPreview
| ImGuiColorEditFlags_AlphaPreviewHalf
));
12297 MarkItemValueChanged(id
);
12302 bool ImGui::ColorEdit3(const char* label
, float col
[3], ImGuiColorEditFlags flags
)
12304 return ColorEdit4(label
, col
, flags
| ImGuiColorEditFlags_NoAlpha
);
12307 void ImGui::ColorEditOptionsPopup(const float* col
, ImGuiColorEditFlags flags
)
12309 bool allow_opt_inputs
= !(flags
& ImGuiColorEditFlags__InputsMask
);
12310 bool allow_opt_datatype
= !(flags
& ImGuiColorEditFlags__DataTypeMask
);
12311 if ((!allow_opt_inputs
&& !allow_opt_datatype
) || !BeginPopup("context"))
12313 ImGuiContext
& g
= *GImGui
;
12314 ImGuiColorEditFlags opts
= g
.ColorEditOptions
;
12315 if (allow_opt_inputs
)
12317 if (RadioButton("RGB", (opts
& ImGuiColorEditFlags_RGB
) != 0)) opts
= (opts
& ~ImGuiColorEditFlags__InputsMask
) | ImGuiColorEditFlags_RGB
;
12318 if (RadioButton("HSV", (opts
& ImGuiColorEditFlags_HSV
) != 0)) opts
= (opts
& ~ImGuiColorEditFlags__InputsMask
) | ImGuiColorEditFlags_HSV
;
12319 if (RadioButton("HEX", (opts
& ImGuiColorEditFlags_HEX
) != 0)) opts
= (opts
& ~ImGuiColorEditFlags__InputsMask
) | ImGuiColorEditFlags_HEX
;
12321 if (allow_opt_datatype
)
12323 if (allow_opt_inputs
) Separator();
12324 if (RadioButton("0..255", (opts
& ImGuiColorEditFlags_Uint8
) != 0)) opts
= (opts
& ~ImGuiColorEditFlags__DataTypeMask
) | ImGuiColorEditFlags_Uint8
;
12325 if (RadioButton("0.00..1.00", (opts
& ImGuiColorEditFlags_Float
) != 0)) opts
= (opts
& ~ImGuiColorEditFlags__DataTypeMask
) | ImGuiColorEditFlags_Float
;
12328 if (allow_opt_inputs
|| allow_opt_datatype
)
12330 if (Button("Copy as..", ImVec2(-1,0)))
12332 if (BeginPopup("Copy"))
12334 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]);
12336 ImFormatString(buf
, IM_ARRAYSIZE(buf
), "(%.3ff, %.3ff, %.3ff, %.3ff)", col
[0], col
[1], col
[2], (flags
& ImGuiColorEditFlags_NoAlpha
) ? 1.0f
: col
[3]);
12337 if (Selectable(buf
))
12338 SetClipboardText(buf
);
12339 ImFormatString(buf
, IM_ARRAYSIZE(buf
), "(%d,%d,%d,%d)", cr
, cg
, cb
, ca
);
12340 if (Selectable(buf
))
12341 SetClipboardText(buf
);
12342 if (flags
& ImGuiColorEditFlags_NoAlpha
)
12343 ImFormatString(buf
, IM_ARRAYSIZE(buf
), "0x%02X%02X%02X", cr
, cg
, cb
);
12345 ImFormatString(buf
, IM_ARRAYSIZE(buf
), "0x%02X%02X%02X%02X", cr
, cg
, cb
, ca
);
12346 if (Selectable(buf
))
12347 SetClipboardText(buf
);
12351 g
.ColorEditOptions
= opts
;
12355 static void ColorPickerOptionsPopup(ImGuiColorEditFlags flags
, const float* ref_col
)
12357 bool allow_opt_picker
= !(flags
& ImGuiColorEditFlags__PickerMask
);
12358 bool allow_opt_alpha_bar
= !(flags
& ImGuiColorEditFlags_NoAlpha
) && !(flags
& ImGuiColorEditFlags_AlphaBar
);
12359 if ((!allow_opt_picker
&& !allow_opt_alpha_bar
) || !ImGui::BeginPopup("context"))
12361 ImGuiContext
& g
= *GImGui
;
12362 if (allow_opt_picker
)
12364 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
12365 ImGui::PushItemWidth(picker_size
.x
);
12366 for (int picker_type
= 0; picker_type
< 2; picker_type
++)
12368 // Draw small/thumbnail version of each picker type (over an invisible button for selection)
12369 if (picker_type
> 0) ImGui::Separator();
12370 ImGui::PushID(picker_type
);
12371 ImGuiColorEditFlags picker_flags
= ImGuiColorEditFlags_NoInputs
|ImGuiColorEditFlags_NoOptions
|ImGuiColorEditFlags_NoLabel
|ImGuiColorEditFlags_NoSidePreview
|(flags
& ImGuiColorEditFlags_NoAlpha
);
12372 if (picker_type
== 0) picker_flags
|= ImGuiColorEditFlags_PickerHueBar
;
12373 if (picker_type
== 1) picker_flags
|= ImGuiColorEditFlags_PickerHueWheel
;
12374 ImVec2 backup_pos
= ImGui::GetCursorScreenPos();
12375 if (ImGui::Selectable("##selectable", false, 0, picker_size
)) // By default, Selectable() is closing popup
12376 g
.ColorEditOptions
= (g
.ColorEditOptions
& ~ImGuiColorEditFlags__PickerMask
) | (picker_flags
& ImGuiColorEditFlags__PickerMask
);
12377 ImGui::SetCursorScreenPos(backup_pos
);
12378 ImVec4 dummy_ref_col
;
12379 memcpy(&dummy_ref_col
.x
, ref_col
, sizeof(float) * (picker_flags
& ImGuiColorEditFlags_NoAlpha
? 3 : 4));
12380 ImGui::ColorPicker4("##dummypicker", &dummy_ref_col
.x
, picker_flags
);
12383 ImGui::PopItemWidth();
12385 if (allow_opt_alpha_bar
)
12387 if (allow_opt_picker
) ImGui::Separator();
12388 ImGui::CheckboxFlags("Alpha Bar", (unsigned int*)&g
.ColorEditOptions
, ImGuiColorEditFlags_AlphaBar
);
12393 // Edit colors components (each component in 0.0f..1.0f range).
12394 // See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
12395 // 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.
12396 bool ImGui::ColorEdit4(const char* label
, float col
[4], ImGuiColorEditFlags flags
)
12398 ImGuiWindow
* window
= GetCurrentWindow();
12399 if (window
->SkipItems
)
12402 ImGuiContext
& g
= *GImGui
;
12403 const ImGuiStyle
& style
= g
.Style
;
12404 const float square_sz
= GetFrameHeight();
12405 const float w_extra
= (flags
& ImGuiColorEditFlags_NoSmallPreview
) ? 0.0f
: (square_sz
+ style
.ItemInnerSpacing
.x
);
12406 const float w_items_all
= CalcItemWidth() - w_extra
;
12407 const char* label_display_end
= FindRenderedTextEnd(label
);
12412 // If we're not showing any slider there's no point in doing any HSV conversions
12413 const ImGuiColorEditFlags flags_untouched
= flags
;
12414 if (flags
& ImGuiColorEditFlags_NoInputs
)
12415 flags
= (flags
& (~ImGuiColorEditFlags__InputsMask
)) | ImGuiColorEditFlags_RGB
| ImGuiColorEditFlags_NoOptions
;
12417 // Context menu: display and modify options (before defaults are applied)
12418 if (!(flags
& ImGuiColorEditFlags_NoOptions
))
12419 ColorEditOptionsPopup(col
, flags
);
12421 // Read stored options
12422 if (!(flags
& ImGuiColorEditFlags__InputsMask
))
12423 flags
|= (g
.ColorEditOptions
& ImGuiColorEditFlags__InputsMask
);
12424 if (!(flags
& ImGuiColorEditFlags__DataTypeMask
))
12425 flags
|= (g
.ColorEditOptions
& ImGuiColorEditFlags__DataTypeMask
);
12426 if (!(flags
& ImGuiColorEditFlags__PickerMask
))
12427 flags
|= (g
.ColorEditOptions
& ImGuiColorEditFlags__PickerMask
);
12428 flags
|= (g
.ColorEditOptions
& ~(ImGuiColorEditFlags__InputsMask
| ImGuiColorEditFlags__DataTypeMask
| ImGuiColorEditFlags__PickerMask
));
12430 const bool alpha
= (flags
& ImGuiColorEditFlags_NoAlpha
) == 0;
12431 const bool hdr
= (flags
& ImGuiColorEditFlags_HDR
) != 0;
12432 const int components
= alpha
? 4 : 3;
12434 // Convert to the formats we need
12435 float f
[4] = { col
[0], col
[1], col
[2], alpha
? col
[3] : 1.0f
};
12436 if (flags
& ImGuiColorEditFlags_HSV
)
12437 ColorConvertRGBtoHSV(f
[0], f
[1], f
[2], f
[0], f
[1], f
[2]);
12438 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]) };
12440 bool value_changed
= false;
12441 bool value_changed_as_float
= false;
12443 if ((flags
& (ImGuiColorEditFlags_RGB
| ImGuiColorEditFlags_HSV
)) != 0 && (flags
& ImGuiColorEditFlags_NoInputs
) == 0)
12445 // RGB/HSV 0..255 Sliders
12446 const float w_item_one
= ImMax(1.0f
, (float)(int)((w_items_all
- (style
.ItemInnerSpacing
.x
) * (components
-1)) / (float)components
));
12447 const float w_item_last
= ImMax(1.0f
, (float)(int)(w_items_all
- (w_item_one
+ style
.ItemInnerSpacing
.x
) * (components
-1)));
12449 const bool hide_prefix
= (w_item_one
<= CalcTextSize((flags
& ImGuiColorEditFlags_Float
) ? "M:0.000" : "M:000").x
);
12450 const char* ids
[4] = { "##X", "##Y", "##Z", "##W" };
12451 const char* fmt_table_int
[3][4] =
12453 { "%3d", "%3d", "%3d", "%3d" }, // Short display
12454 { "R:%3d", "G:%3d", "B:%3d", "A:%3d" }, // Long display for RGBA
12455 { "H:%3d", "S:%3d", "V:%3d", "A:%3d" } // Long display for HSVA
12457 const char* fmt_table_float
[3][4] =
12459 { "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, // Short display
12460 { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA
12461 { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA
12463 const int fmt_idx
= hide_prefix
? 0 : (flags
& ImGuiColorEditFlags_HSV
) ? 2 : 1;
12465 PushItemWidth(w_item_one
);
12466 for (int n
= 0; n
< components
; n
++)
12469 SameLine(0, style
.ItemInnerSpacing
.x
);
12470 if (n
+ 1 == components
)
12471 PushItemWidth(w_item_last
);
12472 if (flags
& ImGuiColorEditFlags_Float
)
12473 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
]);
12475 value_changed
|= DragInt(ids
[n
], &i
[n
], 1.0f
, 0, hdr
? 0 : 255, fmt_table_int
[fmt_idx
][n
]);
12476 if (!(flags
& ImGuiColorEditFlags_NoOptions
))
12477 OpenPopupOnItemClick("context");
12482 else if ((flags
& ImGuiColorEditFlags_HEX
) != 0 && (flags
& ImGuiColorEditFlags_NoInputs
) == 0)
12484 // RGB Hexadecimal Input
12487 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));
12489 ImFormatString(buf
, IM_ARRAYSIZE(buf
), "#%02X%02X%02X", ImClamp(i
[0],0,255), ImClamp(i
[1],0,255), ImClamp(i
[2],0,255));
12490 PushItemWidth(w_items_all
);
12491 if (InputText("##Text", buf
, IM_ARRAYSIZE(buf
), ImGuiInputTextFlags_CharsHexadecimal
| ImGuiInputTextFlags_CharsUppercase
))
12493 value_changed
= true;
12495 while (*p
== '#' || ImCharIsBlankA(*p
))
12497 i
[0] = i
[1] = i
[2] = i
[3] = 0;
12499 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)
12501 sscanf(p
, "%02X%02X%02X", (unsigned int*)&i
[0], (unsigned int*)&i
[1], (unsigned int*)&i
[2]);
12503 if (!(flags
& ImGuiColorEditFlags_NoOptions
))
12504 OpenPopupOnItemClick("context");
12508 ImGuiWindow
* picker_active_window
= NULL
;
12509 if (!(flags
& ImGuiColorEditFlags_NoSmallPreview
))
12511 if (!(flags
& ImGuiColorEditFlags_NoInputs
))
12512 SameLine(0, style
.ItemInnerSpacing
.x
);
12514 const ImVec4
col_v4(col
[0], col
[1], col
[2], alpha
? col
[3] : 1.0f
);
12515 if (ColorButton("##ColorButton", col_v4
, flags
))
12517 if (!(flags
& ImGuiColorEditFlags_NoPicker
))
12519 // Store current color and open a picker
12520 g
.ColorPickerRef
= col_v4
;
12521 OpenPopup("picker");
12522 SetNextWindowPos(window
->DC
.LastItemRect
.GetBL() + ImVec2(-1,style
.ItemSpacing
.y
));
12525 if (!(flags
& ImGuiColorEditFlags_NoOptions
))
12526 OpenPopupOnItemClick("context");
12528 if (BeginPopup("picker"))
12530 picker_active_window
= g
.CurrentWindow
;
12531 if (label
!= label_display_end
)
12533 TextUnformatted(label
, label_display_end
);
12536 ImGuiColorEditFlags picker_flags_to_forward
= ImGuiColorEditFlags__DataTypeMask
| ImGuiColorEditFlags__PickerMask
| ImGuiColorEditFlags_HDR
| ImGuiColorEditFlags_NoAlpha
| ImGuiColorEditFlags_AlphaBar
;
12537 ImGuiColorEditFlags picker_flags
= (flags_untouched
& picker_flags_to_forward
) | ImGuiColorEditFlags__InputsMask
| ImGuiColorEditFlags_NoLabel
| ImGuiColorEditFlags_AlphaPreviewHalf
;
12538 PushItemWidth(square_sz
* 12.0f
); // Use 256 + bar sizes?
12539 value_changed
|= ColorPicker4("##picker", col
, picker_flags
, &g
.ColorPickerRef
.x
);
12545 if (label
!= label_display_end
&& !(flags
& ImGuiColorEditFlags_NoLabel
))
12547 SameLine(0, style
.ItemInnerSpacing
.x
);
12548 TextUnformatted(label
, label_display_end
);
12552 if (picker_active_window
== NULL
)
12554 if (!value_changed_as_float
)
12555 for (int n
= 0; n
< 4; n
++)
12556 f
[n
] = i
[n
] / 255.0f
;
12557 if (flags
& ImGuiColorEditFlags_HSV
)
12558 ColorConvertHSVtoRGB(f
[0], f
[1], f
[2], f
[0], f
[1], f
[2]);
12572 // Drag and Drop Target
12573 // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test.
12574 if ((window
->DC
.LastItemStatusFlags
& ImGuiItemStatusFlags_HoveredRect
) && !(flags
& ImGuiColorEditFlags_NoDragDrop
) && BeginDragDropTarget())
12576 if (const ImGuiPayload
* payload
= AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F
))
12578 memcpy((float*)col
, payload
->Data
, sizeof(float) * 3);
12579 value_changed
= true;
12581 if (const ImGuiPayload
* payload
= AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F
))
12583 memcpy((float*)col
, payload
->Data
, sizeof(float) * components
);
12584 value_changed
= true;
12586 EndDragDropTarget();
12589 // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4().
12590 if (picker_active_window
&& g
.ActiveId
!= 0 && g
.ActiveIdWindow
== picker_active_window
)
12591 window
->DC
.LastItemId
= g
.ActiveId
;
12594 MarkItemValueChanged(window
->DC
.LastItemId
);
12596 return value_changed
;
12599 bool ImGui::ColorPicker3(const char* label
, float col
[3], ImGuiColorEditFlags flags
)
12601 float col4
[4] = { col
[0], col
[1], col
[2], 1.0f
};
12602 if (!ColorPicker4(label
, col4
, flags
| ImGuiColorEditFlags_NoAlpha
))
12604 col
[0] = col4
[0]; col
[1] = col4
[1]; col
[2] = col4
[2];
12608 static void RenderArrowsForVerticalBar(ImDrawList
* draw_list
, ImVec2 pos
, ImVec2 half_sz
, float bar_w
)
12610 ImGui::RenderArrowPointingAt(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
);
12611 ImGui::RenderArrowPointingAt(draw_list
, ImVec2(pos
.x
+ half_sz
.x
, pos
.y
), half_sz
, ImGuiDir_Right
, IM_COL32_WHITE
);
12612 ImGui::RenderArrowPointingAt(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
);
12613 ImGui::RenderArrowPointingAt(draw_list
, ImVec2(pos
.x
+ bar_w
- half_sz
.x
, pos
.y
), half_sz
, ImGuiDir_Left
, IM_COL32_WHITE
);
12617 // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
12618 // 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..)
12619 bool ImGui::ColorPicker4(const char* label
, float col
[4], ImGuiColorEditFlags flags
, const float* ref_col
)
12621 ImGuiContext
& g
= *GImGui
;
12622 ImGuiWindow
* window
= GetCurrentWindow();
12623 ImDrawList
* draw_list
= window
->DrawList
;
12625 ImGuiStyle
& style
= g
.Style
;
12626 ImGuiIO
& io
= g
.IO
;
12631 if (!(flags
& ImGuiColorEditFlags_NoSidePreview
))
12632 flags
|= ImGuiColorEditFlags_NoSmallPreview
;
12634 // Context menu: display and store options.
12635 if (!(flags
& ImGuiColorEditFlags_NoOptions
))
12636 ColorPickerOptionsPopup(flags
, col
);
12638 // Read stored options
12639 if (!(flags
& ImGuiColorEditFlags__PickerMask
))
12640 flags
|= ((g
.ColorEditOptions
& ImGuiColorEditFlags__PickerMask
) ? g
.ColorEditOptions
: ImGuiColorEditFlags__OptionsDefault
) & ImGuiColorEditFlags__PickerMask
;
12641 IM_ASSERT(ImIsPowerOfTwo((int)(flags
& ImGuiColorEditFlags__PickerMask
))); // Check that only 1 is selected
12642 if (!(flags
& ImGuiColorEditFlags_NoOptions
))
12643 flags
|= (g
.ColorEditOptions
& ImGuiColorEditFlags_AlphaBar
);
12646 int components
= (flags
& ImGuiColorEditFlags_NoAlpha
) ? 3 : 4;
12647 bool alpha_bar
= (flags
& ImGuiColorEditFlags_AlphaBar
) && !(flags
& ImGuiColorEditFlags_NoAlpha
);
12648 ImVec2 picker_pos
= window
->DC
.CursorPos
;
12649 float square_sz
= GetFrameHeight();
12650 float bars_width
= square_sz
; // Arbitrary smallish width of Hue/Alpha picking bars
12651 float sv_picker_size
= ImMax(bars_width
* 1, CalcItemWidth() - (alpha_bar
? 2 : 1) * (bars_width
+ style
.ItemInnerSpacing
.x
)); // Saturation/Value picking box
12652 float bar0_pos_x
= picker_pos
.x
+ sv_picker_size
+ style
.ItemInnerSpacing
.x
;
12653 float bar1_pos_x
= bar0_pos_x
+ bars_width
+ style
.ItemInnerSpacing
.x
;
12654 float bars_triangles_half_sz
= (float)(int)(bars_width
* 0.20f
);
12656 float backup_initial_col
[4];
12657 memcpy(backup_initial_col
, col
, components
* sizeof(float));
12659 float wheel_thickness
= sv_picker_size
* 0.08f
;
12660 float wheel_r_outer
= sv_picker_size
* 0.50f
;
12661 float wheel_r_inner
= wheel_r_outer
- wheel_thickness
;
12662 ImVec2
wheel_center(picker_pos
.x
+ (sv_picker_size
+ bars_width
)*0.5f
, picker_pos
.y
+ sv_picker_size
*0.5f
);
12664 // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic.
12665 float triangle_r
= wheel_r_inner
- (int)(sv_picker_size
* 0.027f
);
12666 ImVec2 triangle_pa
= ImVec2(triangle_r
, 0.0f
); // Hue point.
12667 ImVec2 triangle_pb
= ImVec2(triangle_r
* -0.5f
, triangle_r
* -0.866025f
); // Black point.
12668 ImVec2 triangle_pc
= ImVec2(triangle_r
* -0.5f
, triangle_r
* +0.866025f
); // White point.
12671 ColorConvertRGBtoHSV(col
[0], col
[1], col
[2], H
, S
, V
);
12673 bool value_changed
= false, value_changed_h
= false, value_changed_sv
= false;
12675 PushItemFlag(ImGuiItemFlags_NoNav
, true);
12676 if (flags
& ImGuiColorEditFlags_PickerHueWheel
)
12678 // Hue wheel + SV triangle logic
12679 InvisibleButton("hsv", ImVec2(sv_picker_size
+ style
.ItemInnerSpacing
.x
+ bars_width
, sv_picker_size
));
12680 if (IsItemActive())
12682 ImVec2 initial_off
= g
.IO
.MouseClickedPos
[0] - wheel_center
;
12683 ImVec2 current_off
= g
.IO
.MousePos
- wheel_center
;
12684 float initial_dist2
= ImLengthSqr(initial_off
);
12685 if (initial_dist2
>= (wheel_r_inner
-1)*(wheel_r_inner
-1) && initial_dist2
<= (wheel_r_outer
+1)*(wheel_r_outer
+1))
12687 // Interactive with Hue wheel
12688 H
= ImAtan2(current_off
.y
, current_off
.x
) / IM_PI
*0.5f
;
12691 value_changed
= value_changed_h
= true;
12693 float cos_hue_angle
= ImCos(-H
* 2.0f
* IM_PI
);
12694 float sin_hue_angle
= ImSin(-H
* 2.0f
* IM_PI
);
12695 if (ImTriangleContainsPoint(triangle_pa
, triangle_pb
, triangle_pc
, ImRotate(initial_off
, cos_hue_angle
, sin_hue_angle
)))
12697 // Interacting with SV triangle
12698 ImVec2 current_off_unrotated
= ImRotate(current_off
, cos_hue_angle
, sin_hue_angle
);
12699 if (!ImTriangleContainsPoint(triangle_pa
, triangle_pb
, triangle_pc
, current_off_unrotated
))
12700 current_off_unrotated
= ImTriangleClosestPoint(triangle_pa
, triangle_pb
, triangle_pc
, current_off_unrotated
);
12702 ImTriangleBarycentricCoords(triangle_pa
, triangle_pb
, triangle_pc
, current_off_unrotated
, uu
, vv
, ww
);
12703 V
= ImClamp(1.0f
- vv
, 0.0001f
, 1.0f
);
12704 S
= ImClamp(uu
/ V
, 0.0001f
, 1.0f
);
12705 value_changed
= value_changed_sv
= true;
12708 if (!(flags
& ImGuiColorEditFlags_NoOptions
))
12709 OpenPopupOnItemClick("context");
12711 else if (flags
& ImGuiColorEditFlags_PickerHueBar
)
12713 // SV rectangle logic
12714 InvisibleButton("sv", ImVec2(sv_picker_size
, sv_picker_size
));
12715 if (IsItemActive())
12717 S
= ImSaturate((io
.MousePos
.x
- picker_pos
.x
) / (sv_picker_size
-1));
12718 V
= 1.0f
- ImSaturate((io
.MousePos
.y
- picker_pos
.y
) / (sv_picker_size
-1));
12719 value_changed
= value_changed_sv
= true;
12721 if (!(flags
& ImGuiColorEditFlags_NoOptions
))
12722 OpenPopupOnItemClick("context");
12725 SetCursorScreenPos(ImVec2(bar0_pos_x
, picker_pos
.y
));
12726 InvisibleButton("hue", ImVec2(bars_width
, sv_picker_size
));
12727 if (IsItemActive())
12729 H
= ImSaturate((io
.MousePos
.y
- picker_pos
.y
) / (sv_picker_size
-1));
12730 value_changed
= value_changed_h
= true;
12737 SetCursorScreenPos(ImVec2(bar1_pos_x
, picker_pos
.y
));
12738 InvisibleButton("alpha", ImVec2(bars_width
, sv_picker_size
));
12739 if (IsItemActive())
12741 col
[3] = 1.0f
- ImSaturate((io
.MousePos
.y
- picker_pos
.y
) / (sv_picker_size
-1));
12742 value_changed
= true;
12745 PopItemFlag(); // ImGuiItemFlags_NoNav
12747 if (!(flags
& ImGuiColorEditFlags_NoSidePreview
))
12749 SameLine(0, style
.ItemInnerSpacing
.x
);
12753 if (!(flags
& ImGuiColorEditFlags_NoLabel
))
12755 const char* label_display_end
= FindRenderedTextEnd(label
);
12756 if (label
!= label_display_end
)
12758 if ((flags
& ImGuiColorEditFlags_NoSidePreview
))
12759 SameLine(0, style
.ItemInnerSpacing
.x
);
12760 TextUnformatted(label
, label_display_end
);
12764 if (!(flags
& ImGuiColorEditFlags_NoSidePreview
))
12766 PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus
, true);
12767 ImVec4
col_v4(col
[0], col
[1], col
[2], (flags
& ImGuiColorEditFlags_NoAlpha
) ? 1.0f
: col
[3]);
12768 if ((flags
& ImGuiColorEditFlags_NoLabel
))
12770 ColorButton("##current", col_v4
, (flags
& (ImGuiColorEditFlags_HDR
|ImGuiColorEditFlags_AlphaPreview
|ImGuiColorEditFlags_AlphaPreviewHalf
|ImGuiColorEditFlags_NoTooltip
)), ImVec2(square_sz
* 3, square_sz
* 2));
12771 if (ref_col
!= NULL
)
12774 ImVec4
ref_col_v4(ref_col
[0], ref_col
[1], ref_col
[2], (flags
& ImGuiColorEditFlags_NoAlpha
) ? 1.0f
: ref_col
[3]);
12775 if (ColorButton("##original", ref_col_v4
, (flags
& (ImGuiColorEditFlags_HDR
|ImGuiColorEditFlags_AlphaPreview
|ImGuiColorEditFlags_AlphaPreviewHalf
|ImGuiColorEditFlags_NoTooltip
)), ImVec2(square_sz
* 3, square_sz
* 2)))
12777 memcpy(col
, ref_col
, components
* sizeof(float));
12778 value_changed
= true;
12785 // Convert back color to RGB
12786 if (value_changed_h
|| value_changed_sv
)
12787 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]);
12789 // R,G,B and H,S,V slider color editor
12790 if ((flags
& ImGuiColorEditFlags_NoInputs
) == 0)
12792 PushItemWidth((alpha_bar
? bar1_pos_x
: bar0_pos_x
) + bars_width
- picker_pos
.x
);
12793 ImGuiColorEditFlags sub_flags_to_forward
= ImGuiColorEditFlags__DataTypeMask
| ImGuiColorEditFlags_HDR
| ImGuiColorEditFlags_NoAlpha
| ImGuiColorEditFlags_NoOptions
| ImGuiColorEditFlags_NoSmallPreview
| ImGuiColorEditFlags_AlphaPreview
| ImGuiColorEditFlags_AlphaPreviewHalf
;
12794 ImGuiColorEditFlags sub_flags
= (flags
& sub_flags_to_forward
) | ImGuiColorEditFlags_NoPicker
;
12795 if (flags
& ImGuiColorEditFlags_RGB
|| (flags
& ImGuiColorEditFlags__InputsMask
) == 0)
12796 value_changed
|= ColorEdit4("##rgb", col
, sub_flags
| ImGuiColorEditFlags_RGB
);
12797 if (flags
& ImGuiColorEditFlags_HSV
|| (flags
& ImGuiColorEditFlags__InputsMask
) == 0)
12798 value_changed
|= ColorEdit4("##hsv", col
, sub_flags
| ImGuiColorEditFlags_HSV
);
12799 if (flags
& ImGuiColorEditFlags_HEX
|| (flags
& ImGuiColorEditFlags__InputsMask
) == 0)
12800 value_changed
|= ColorEdit4("##hex", col
, sub_flags
| ImGuiColorEditFlags_HEX
);
12804 // Try to cancel hue wrap (after ColorEdit), if any
12807 float new_H
, new_S
, new_V
;
12808 ColorConvertRGBtoHSV(col
[0], col
[1], col
[2], new_H
, new_S
, new_V
);
12809 if (new_H
<= 0 && H
> 0)
12811 if (new_V
<= 0 && V
!= new_V
)
12812 ColorConvertHSVtoRGB(H
, S
, new_V
<= 0 ? V
* 0.5f
: new_V
, col
[0], col
[1], col
[2]);
12813 else if (new_S
<= 0)
12814 ColorConvertHSVtoRGB(H
, new_S
<= 0 ? S
* 0.5f
: new_S
, new_V
, col
[0], col
[1], col
[2]);
12818 ImVec4
hue_color_f(1, 1, 1, 1); ColorConvertHSVtoRGB(H
, 1, 1, hue_color_f
.x
, hue_color_f
.y
, hue_color_f
.z
);
12819 ImU32 hue_color32
= ColorConvertFloat4ToU32(hue_color_f
);
12820 ImU32 col32_no_alpha
= ColorConvertFloat4ToU32(ImVec4(col
[0], col
[1], col
[2], 1.0f
));
12822 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) };
12823 ImVec2 sv_cursor_pos
;
12825 if (flags
& ImGuiColorEditFlags_PickerHueWheel
)
12827 // Render Hue Wheel
12828 const float aeps
= 1.5f
/ wheel_r_outer
; // Half a pixel arc length in radians (2pi cancels out).
12829 const int segment_per_arc
= ImMax(4, (int)wheel_r_outer
/ 12);
12830 for (int n
= 0; n
< 6; n
++)
12832 const float a0
= (n
) /6.0f
* 2.0f
* IM_PI
- aeps
;
12833 const float a1
= (n
+1.0f
)/6.0f
* 2.0f
* IM_PI
+ aeps
;
12834 const int vert_start_idx
= draw_list
->VtxBuffer
.Size
;
12835 draw_list
->PathArcTo(wheel_center
, (wheel_r_inner
+ wheel_r_outer
)*0.5f
, a0
, a1
, segment_per_arc
);
12836 draw_list
->PathStroke(IM_COL32_WHITE
, false, wheel_thickness
);
12837 const int vert_end_idx
= draw_list
->VtxBuffer
.Size
;
12839 // Paint colors over existing vertices
12840 ImVec2
gradient_p0(wheel_center
.x
+ ImCos(a0
) * wheel_r_inner
, wheel_center
.y
+ ImSin(a0
) * wheel_r_inner
);
12841 ImVec2
gradient_p1(wheel_center
.x
+ ImCos(a1
) * wheel_r_inner
, wheel_center
.y
+ ImSin(a1
) * wheel_r_inner
);
12842 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]);
12845 // Render Cursor + preview on Hue Wheel
12846 float cos_hue_angle
= ImCos(H
* 2.0f
* IM_PI
);
12847 float sin_hue_angle
= ImSin(H
* 2.0f
* IM_PI
);
12848 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
);
12849 float hue_cursor_rad
= value_changed_h
? wheel_thickness
* 0.65f
: wheel_thickness
* 0.55f
;
12850 int hue_cursor_segments
= ImClamp((int)(hue_cursor_rad
/ 1.4f
), 9, 32);
12851 draw_list
->AddCircleFilled(hue_cursor_pos
, hue_cursor_rad
, hue_color32
, hue_cursor_segments
);
12852 draw_list
->AddCircle(hue_cursor_pos
, hue_cursor_rad
+1, IM_COL32(128,128,128,255), hue_cursor_segments
);
12853 draw_list
->AddCircle(hue_cursor_pos
, hue_cursor_rad
, IM_COL32_WHITE
, hue_cursor_segments
);
12855 // Render SV triangle (rotated according to hue)
12856 ImVec2 tra
= wheel_center
+ ImRotate(triangle_pa
, cos_hue_angle
, sin_hue_angle
);
12857 ImVec2 trb
= wheel_center
+ ImRotate(triangle_pb
, cos_hue_angle
, sin_hue_angle
);
12858 ImVec2 trc
= wheel_center
+ ImRotate(triangle_pc
, cos_hue_angle
, sin_hue_angle
);
12859 ImVec2 uv_white
= GetFontTexUvWhitePixel();
12860 draw_list
->PrimReserve(6, 6);
12861 draw_list
->PrimVtx(tra
, uv_white
, hue_color32
);
12862 draw_list
->PrimVtx(trb
, uv_white
, hue_color32
);
12863 draw_list
->PrimVtx(trc
, uv_white
, IM_COL32_WHITE
);
12864 draw_list
->PrimVtx(tra
, uv_white
, IM_COL32_BLACK_TRANS
);
12865 draw_list
->PrimVtx(trb
, uv_white
, IM_COL32_BLACK
);
12866 draw_list
->PrimVtx(trc
, uv_white
, IM_COL32_BLACK_TRANS
);
12867 draw_list
->AddTriangle(tra
, trb
, trc
, IM_COL32(128,128,128,255), 1.5f
);
12868 sv_cursor_pos
= ImLerp(ImLerp(trc
, tra
, ImSaturate(S
)), trb
, ImSaturate(1 - V
));
12870 else if (flags
& ImGuiColorEditFlags_PickerHueBar
)
12872 // Render SV Square
12873 draw_list
->AddRectFilledMultiColor(picker_pos
, picker_pos
+ ImVec2(sv_picker_size
,sv_picker_size
), IM_COL32_WHITE
, hue_color32
, hue_color32
, IM_COL32_WHITE
);
12874 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
);
12875 RenderFrameBorder(picker_pos
, picker_pos
+ ImVec2(sv_picker_size
,sv_picker_size
), 0.0f
);
12876 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
12877 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);
12880 for (int i
= 0; i
< 6; ++i
)
12881 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]);
12882 float bar0_line_y
= (float)(int)(picker_pos
.y
+ H
* sv_picker_size
+ 0.5f
);
12883 RenderFrameBorder(ImVec2(bar0_pos_x
, picker_pos
.y
), ImVec2(bar0_pos_x
+ bars_width
, picker_pos
.y
+ sv_picker_size
), 0.0f
);
12884 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
);
12887 // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range)
12888 float sv_cursor_rad
= value_changed_sv
? 10.0f
: 6.0f
;
12889 draw_list
->AddCircleFilled(sv_cursor_pos
, sv_cursor_rad
, col32_no_alpha
, 12);
12890 draw_list
->AddCircle(sv_cursor_pos
, sv_cursor_rad
+1, IM_COL32(128,128,128,255), 12);
12891 draw_list
->AddCircle(sv_cursor_pos
, sv_cursor_rad
, IM_COL32_WHITE
, 12);
12893 // Render alpha bar
12896 float alpha
= ImSaturate(col
[3]);
12897 ImRect
bar1_bb(bar1_pos_x
, picker_pos
.y
, bar1_pos_x
+ bars_width
, picker_pos
.y
+ sv_picker_size
);
12898 RenderColorRectWithAlphaCheckerboard(bar1_bb
.Min
, bar1_bb
.Max
, IM_COL32(0,0,0,0), bar1_bb
.GetWidth() / 2.0f
, ImVec2(0.0f
, 0.0f
));
12899 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
);
12900 float bar1_line_y
= (float)(int)(picker_pos
.y
+ (1.0f
- alpha
) * sv_picker_size
+ 0.5f
);
12901 RenderFrameBorder(bar1_bb
.Min
, bar1_bb
.Max
, 0.0f
);
12902 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
);
12907 if (value_changed
&& memcmp(backup_initial_col
, col
, components
* sizeof(float)) == 0)
12908 value_changed
= false;
12910 MarkItemValueChanged(window
->DC
.LastItemId
);
12914 return value_changed
;
12917 // Horizontal/vertical separating line
12918 void ImGui::Separator()
12920 ImGuiWindow
* window
= GetCurrentWindow();
12921 if (window
->SkipItems
)
12923 ImGuiContext
& g
= *GImGui
;
12925 // Those flags should eventually be overridable by the user
12926 ImGuiSeparatorFlags flags
= (window
->DC
.LayoutType
== ImGuiLayoutType_Horizontal
) ? ImGuiSeparatorFlags_Vertical
: ImGuiSeparatorFlags_Horizontal
;
12927 IM_ASSERT(ImIsPowerOfTwo((int)(flags
& (ImGuiSeparatorFlags_Horizontal
| ImGuiSeparatorFlags_Vertical
)))); // Check that only 1 option is selected
12928 if (flags
& ImGuiSeparatorFlags_Vertical
)
12930 VerticalSeparator();
12934 // Horizontal Separator
12935 if (window
->DC
.ColumnsSet
)
12938 float x1
= window
->Pos
.x
;
12939 float x2
= window
->Pos
.x
+ window
->Size
.x
;
12940 if (!window
->DC
.GroupStack
.empty())
12941 x1
+= window
->DC
.IndentX
;
12943 const ImRect
bb(ImVec2(x1
, window
->DC
.CursorPos
.y
), ImVec2(x2
, window
->DC
.CursorPos
.y
+1.0f
));
12944 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.
12945 if (!ItemAdd(bb
, 0))
12947 if (window
->DC
.ColumnsSet
)
12948 PushColumnClipRect();
12952 window
->DrawList
->AddLine(bb
.Min
, ImVec2(bb
.Max
.x
,bb
.Min
.y
), GetColorU32(ImGuiCol_Separator
));
12955 LogRenderedText(NULL
, IM_NEWLINE
"--------------------------------");
12957 if (window
->DC
.ColumnsSet
)
12959 PushColumnClipRect();
12960 window
->DC
.ColumnsSet
->LineMinY
= window
->DC
.CursorPos
.y
;
12964 void ImGui::VerticalSeparator()
12966 ImGuiWindow
* window
= GetCurrentWindow();
12967 if (window
->SkipItems
)
12969 ImGuiContext
& g
= *GImGui
;
12971 float y1
= window
->DC
.CursorPos
.y
;
12972 float y2
= window
->DC
.CursorPos
.y
+ window
->DC
.CurrentLineHeight
;
12973 const ImRect
bb(ImVec2(window
->DC
.CursorPos
.x
, y1
), ImVec2(window
->DC
.CursorPos
.x
+ 1.0f
, y2
));
12974 ItemSize(ImVec2(bb
.GetWidth(), 0.0f
));
12975 if (!ItemAdd(bb
, 0))
12978 window
->DrawList
->AddLine(ImVec2(bb
.Min
.x
, bb
.Min
.y
), ImVec2(bb
.Min
.x
, bb
.Max
.y
), GetColorU32(ImGuiCol_Separator
));
12983 // Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise.
12984 bool ImGui::SplitterBehavior(const ImRect
& bb
, ImGuiID id
, ImGuiAxis axis
, float* size1
, float* size2
, float min_size1
, float min_size2
, float hover_extend
, float hover_visibility_delay
)
12986 ImGuiContext
& g
= *GImGui
;
12987 ImGuiWindow
* window
= g
.CurrentWindow
;
12989 const ImGuiItemFlags item_flags_backup
= window
->DC
.ItemFlags
;
12990 window
->DC
.ItemFlags
|= ImGuiItemFlags_NoNav
| ImGuiItemFlags_NoNavDefaultFocus
;
12991 bool item_add
= ItemAdd(bb
, id
);
12992 window
->DC
.ItemFlags
= item_flags_backup
;
12996 bool hovered
, held
;
12997 ImRect bb_interact
= bb
;
12998 bb_interact
.Expand(axis
== ImGuiAxis_Y
? ImVec2(0.0f
, hover_extend
) : ImVec2(hover_extend
, 0.0f
));
12999 ButtonBehavior(bb_interact
, id
, &hovered
, &held
, ImGuiButtonFlags_FlattenChildren
| ImGuiButtonFlags_AllowItemOverlap
);
13000 if (g
.ActiveId
!= id
)
13001 SetItemAllowOverlap();
13003 if (held
|| (g
.HoveredId
== id
&& g
.HoveredIdPreviousFrame
== id
&& g
.HoveredIdTimer
>= hover_visibility_delay
))
13004 SetMouseCursor(axis
== ImGuiAxis_Y
? ImGuiMouseCursor_ResizeNS
: ImGuiMouseCursor_ResizeEW
);
13006 ImRect bb_render
= bb
;
13009 ImVec2 mouse_delta_2d
= g
.IO
.MousePos
- g
.ActiveIdClickOffset
- bb_interact
.Min
;
13010 float mouse_delta
= (axis
== ImGuiAxis_Y
) ? mouse_delta_2d
.y
: mouse_delta_2d
.x
;
13012 // Minimum pane size
13013 if (mouse_delta
< min_size1
- *size1
)
13014 mouse_delta
= min_size1
- *size1
;
13015 if (mouse_delta
> *size2
- min_size2
)
13016 mouse_delta
= *size2
- min_size2
;
13019 *size1
+= mouse_delta
;
13020 *size2
-= mouse_delta
;
13021 bb_render
.Translate((axis
== ImGuiAxis_X
) ? ImVec2(mouse_delta
, 0.0f
) : ImVec2(0.0f
, mouse_delta
));
13023 MarkItemValueChanged(id
);
13027 const ImU32 col
= GetColorU32(held
? ImGuiCol_SeparatorActive
: (hovered
&& g
.HoveredIdTimer
>= hover_visibility_delay
) ? ImGuiCol_SeparatorHovered
: ImGuiCol_Separator
);
13028 window
->DrawList
->AddRectFilled(bb_render
.Min
, bb_render
.Max
, col
, g
.Style
.FrameRounding
);
13033 void ImGui::Spacing()
13035 ImGuiWindow
* window
= GetCurrentWindow();
13036 if (window
->SkipItems
)
13038 ItemSize(ImVec2(0,0));
13041 void ImGui::Dummy(const ImVec2
& size
)
13043 ImGuiWindow
* window
= GetCurrentWindow();
13044 if (window
->SkipItems
)
13047 const ImRect
bb(window
->DC
.CursorPos
, window
->DC
.CursorPos
+ size
);
13052 bool ImGui::IsRectVisible(const ImVec2
& size
)
13054 ImGuiWindow
* window
= GetCurrentWindowRead();
13055 return window
->ClipRect
.Overlaps(ImRect(window
->DC
.CursorPos
, window
->DC
.CursorPos
+ size
));
13058 bool ImGui::IsRectVisible(const ImVec2
& rect_min
, const ImVec2
& rect_max
)
13060 ImGuiWindow
* window
= GetCurrentWindowRead();
13061 return window
->ClipRect
.Overlaps(ImRect(rect_min
, rect_max
));
13064 // 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.)
13065 void ImGui::BeginGroup()
13067 ImGuiContext
& g
= *GImGui
;
13068 ImGuiWindow
* window
= GetCurrentWindow();
13070 window
->DC
.GroupStack
.resize(window
->DC
.GroupStack
.Size
+ 1);
13071 ImGuiGroupData
& group_data
= window
->DC
.GroupStack
.back();
13072 group_data
.BackupCursorPos
= window
->DC
.CursorPos
;
13073 group_data
.BackupCursorMaxPos
= window
->DC
.CursorMaxPos
;
13074 group_data
.BackupIndentX
= window
->DC
.IndentX
;
13075 group_data
.BackupGroupOffsetX
= window
->DC
.GroupOffsetX
;
13076 group_data
.BackupCurrentLineHeight
= window
->DC
.CurrentLineHeight
;
13077 group_data
.BackupCurrentLineTextBaseOffset
= window
->DC
.CurrentLineTextBaseOffset
;
13078 group_data
.BackupLogLinePosY
= window
->DC
.LogLinePosY
;
13079 group_data
.BackupActiveIdIsAlive
= g
.ActiveIdIsAlive
;
13080 group_data
.BackupActiveIdPreviousFrameIsAlive
= g
.ActiveIdPreviousFrameIsAlive
;
13081 group_data
.AdvanceCursor
= true;
13083 window
->DC
.GroupOffsetX
= window
->DC
.CursorPos
.x
- window
->Pos
.x
- window
->DC
.ColumnsOffsetX
;
13084 window
->DC
.IndentX
= window
->DC
.GroupOffsetX
;
13085 window
->DC
.CursorMaxPos
= window
->DC
.CursorPos
;
13086 window
->DC
.CurrentLineHeight
= 0.0f
;
13087 window
->DC
.LogLinePosY
= window
->DC
.CursorPos
.y
- 9999.0f
; // To enforce Log carriage return
13090 void ImGui::EndGroup()
13092 ImGuiContext
& g
= *GImGui
;
13093 ImGuiWindow
* window
= GetCurrentWindow();
13094 IM_ASSERT(!window
->DC
.GroupStack
.empty()); // Mismatched BeginGroup()/EndGroup() calls
13096 ImGuiGroupData
& group_data
= window
->DC
.GroupStack
.back();
13098 ImRect
group_bb(group_data
.BackupCursorPos
, window
->DC
.CursorMaxPos
);
13099 group_bb
.Max
= ImMax(group_bb
.Min
, group_bb
.Max
);
13101 window
->DC
.CursorPos
= group_data
.BackupCursorPos
;
13102 window
->DC
.CursorMaxPos
= ImMax(group_data
.BackupCursorMaxPos
, window
->DC
.CursorMaxPos
);
13103 window
->DC
.IndentX
= group_data
.BackupIndentX
;
13104 window
->DC
.GroupOffsetX
= group_data
.BackupGroupOffsetX
;
13105 window
->DC
.CurrentLineHeight
= group_data
.BackupCurrentLineHeight
;
13106 window
->DC
.CurrentLineTextBaseOffset
= group_data
.BackupCurrentLineTextBaseOffset
;
13107 window
->DC
.LogLinePosY
= window
->DC
.CursorPos
.y
- 9999.0f
; // To enforce Log carriage return
13109 if (group_data
.AdvanceCursor
)
13111 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.
13112 ItemSize(group_bb
.GetSize(), group_data
.BackupCurrentLineTextBaseOffset
);
13113 ItemAdd(group_bb
, 0);
13116 // 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.
13117 // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but put a little more burden on individual widgets.
13118 // (and if you grep for LastItemId you'll notice it is only used in that context.
13119 if (!group_data
.BackupActiveIdIsAlive
&& g
.ActiveIdIsAlive
&& g
.ActiveId
) // && g.ActiveIdWindow->RootWindow == window->RootWindow)
13120 window
->DC
.LastItemId
= g
.ActiveId
;
13121 else if (!group_data
.BackupActiveIdPreviousFrameIsAlive
&& g
.ActiveIdPreviousFrameIsAlive
) // && g.ActiveIdPreviousFrameWindow->RootWindow == window->RootWindow)
13122 window
->DC
.LastItemId
= g
.ActiveIdPreviousFrame
;
13123 window
->DC
.LastItemRect
= group_bb
;
13125 window
->DC
.GroupStack
.pop_back();
13127 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
13130 // Gets back to previous line and continue with horizontal layout
13131 // pos_x == 0 : follow right after previous item
13132 // pos_x != 0 : align to specified x position (relative to window/group left)
13133 // spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0
13134 // spacing_w >= 0 : enforce spacing amount
13135 void ImGui::SameLine(float pos_x
, float spacing_w
)
13137 ImGuiWindow
* window
= GetCurrentWindow();
13138 if (window
->SkipItems
)
13141 ImGuiContext
& g
= *GImGui
;
13144 if (spacing_w
< 0.0f
) spacing_w
= 0.0f
;
13145 window
->DC
.CursorPos
.x
= window
->Pos
.x
- window
->Scroll
.x
+ pos_x
+ spacing_w
+ window
->DC
.GroupOffsetX
+ window
->DC
.ColumnsOffsetX
;
13146 window
->DC
.CursorPos
.y
= window
->DC
.CursorPosPrevLine
.y
;
13150 if (spacing_w
< 0.0f
) spacing_w
= g
.Style
.ItemSpacing
.x
;
13151 window
->DC
.CursorPos
.x
= window
->DC
.CursorPosPrevLine
.x
+ spacing_w
;
13152 window
->DC
.CursorPos
.y
= window
->DC
.CursorPosPrevLine
.y
;
13154 window
->DC
.CurrentLineHeight
= window
->DC
.PrevLineHeight
;
13155 window
->DC
.CurrentLineTextBaseOffset
= window
->DC
.PrevLineTextBaseOffset
;
13158 void ImGui::NewLine()
13160 ImGuiWindow
* window
= GetCurrentWindow();
13161 if (window
->SkipItems
)
13164 ImGuiContext
& g
= *GImGui
;
13165 const ImGuiLayoutType backup_layout_type
= window
->DC
.LayoutType
;
13166 window
->DC
.LayoutType
= ImGuiLayoutType_Vertical
;
13167 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.
13168 ItemSize(ImVec2(0,0));
13170 ItemSize(ImVec2(0.0f
, g
.FontSize
));
13171 window
->DC
.LayoutType
= backup_layout_type
;
13174 void ImGui::NextColumn()
13176 ImGuiWindow
* window
= GetCurrentWindow();
13177 if (window
->SkipItems
|| window
->DC
.ColumnsSet
== NULL
)
13180 ImGuiContext
& g
= *GImGui
;
13184 ImGuiColumnsSet
* columns
= window
->DC
.ColumnsSet
;
13185 columns
->LineMaxY
= ImMax(columns
->LineMaxY
, window
->DC
.CursorPos
.y
);
13186 if (++columns
->Current
< columns
->Count
)
13188 // Columns 1+ cancel out IndentX
13189 window
->DC
.ColumnsOffsetX
= GetColumnOffset(columns
->Current
) - window
->DC
.IndentX
+ g
.Style
.ItemSpacing
.x
;
13190 window
->DrawList
->ChannelsSetCurrent(columns
->Current
);
13194 window
->DC
.ColumnsOffsetX
= 0.0f
;
13195 window
->DrawList
->ChannelsSetCurrent(0);
13196 columns
->Current
= 0;
13197 columns
->LineMinY
= columns
->LineMaxY
;
13199 window
->DC
.CursorPos
.x
= (float)(int)(window
->Pos
.x
+ window
->DC
.IndentX
+ window
->DC
.ColumnsOffsetX
);
13200 window
->DC
.CursorPos
.y
= columns
->LineMinY
;
13201 window
->DC
.CurrentLineHeight
= 0.0f
;
13202 window
->DC
.CurrentLineTextBaseOffset
= 0.0f
;
13204 PushColumnClipRect();
13205 PushItemWidth(GetColumnWidth() * 0.65f
); // FIXME: Move on columns setup
13208 int ImGui::GetColumnIndex()
13210 ImGuiWindow
* window
= GetCurrentWindowRead();
13211 return window
->DC
.ColumnsSet
? window
->DC
.ColumnsSet
->Current
: 0;
13214 int ImGui::GetColumnsCount()
13216 ImGuiWindow
* window
= GetCurrentWindowRead();
13217 return window
->DC
.ColumnsSet
? window
->DC
.ColumnsSet
->Count
: 1;
13220 static float OffsetNormToPixels(const ImGuiColumnsSet
* columns
, float offset_norm
)
13222 return offset_norm
* (columns
->MaxX
- columns
->MinX
);
13225 static float PixelsToOffsetNorm(const ImGuiColumnsSet
* columns
, float offset
)
13227 return offset
/ (columns
->MaxX
- columns
->MinX
);
13230 static inline float GetColumnsRectHalfWidth() { return 4.0f
; }
13232 static float GetDraggedColumnOffset(ImGuiColumnsSet
* columns
, int column_index
)
13234 // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
13235 // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
13236 ImGuiContext
& g
= *GImGui
;
13237 ImGuiWindow
* window
= g
.CurrentWindow
;
13238 IM_ASSERT(column_index
> 0); // We are not supposed to drag column 0.
13239 IM_ASSERT(g
.ActiveId
== columns
->ID
+ ImGuiID(column_index
));
13241 float x
= g
.IO
.MousePos
.x
- g
.ActiveIdClickOffset
.x
+ GetColumnsRectHalfWidth() - window
->Pos
.x
;
13242 x
= ImMax(x
, ImGui::GetColumnOffset(column_index
- 1) + g
.Style
.ColumnsMinSpacing
);
13243 if ((columns
->Flags
& ImGuiColumnsFlags_NoPreserveWidths
))
13244 x
= ImMin(x
, ImGui::GetColumnOffset(column_index
+ 1) - g
.Style
.ColumnsMinSpacing
);
13249 float ImGui::GetColumnOffset(int column_index
)
13251 ImGuiWindow
* window
= GetCurrentWindowRead();
13252 ImGuiColumnsSet
* columns
= window
->DC
.ColumnsSet
;
13253 IM_ASSERT(columns
!= NULL
);
13255 if (column_index
< 0)
13256 column_index
= columns
->Current
;
13257 IM_ASSERT(column_index
< columns
->Columns
.Size
);
13259 const float t
= columns
->Columns
[column_index
].OffsetNorm
;
13260 const float x_offset
= ImLerp(columns
->MinX
, columns
->MaxX
, t
);
13264 static float GetColumnWidthEx(ImGuiColumnsSet
* columns
, int column_index
, bool before_resize
= false)
13266 if (column_index
< 0)
13267 column_index
= columns
->Current
;
13271 offset_norm
= columns
->Columns
[column_index
+ 1].OffsetNormBeforeResize
- columns
->Columns
[column_index
].OffsetNormBeforeResize
;
13273 offset_norm
= columns
->Columns
[column_index
+ 1].OffsetNorm
- columns
->Columns
[column_index
].OffsetNorm
;
13274 return OffsetNormToPixels(columns
, offset_norm
);
13277 float ImGui::GetColumnWidth(int column_index
)
13279 ImGuiWindow
* window
= GetCurrentWindowRead();
13280 ImGuiColumnsSet
* columns
= window
->DC
.ColumnsSet
;
13281 IM_ASSERT(columns
!= NULL
);
13283 if (column_index
< 0)
13284 column_index
= columns
->Current
;
13285 return OffsetNormToPixels(columns
, columns
->Columns
[column_index
+ 1].OffsetNorm
- columns
->Columns
[column_index
].OffsetNorm
);
13288 void ImGui::SetColumnOffset(int column_index
, float offset
)
13290 ImGuiContext
& g
= *GImGui
;
13291 ImGuiWindow
* window
= g
.CurrentWindow
;
13292 ImGuiColumnsSet
* columns
= window
->DC
.ColumnsSet
;
13293 IM_ASSERT(columns
!= NULL
);
13295 if (column_index
< 0)
13296 column_index
= columns
->Current
;
13297 IM_ASSERT(column_index
< columns
->Columns
.Size
);
13299 const bool preserve_width
= !(columns
->Flags
& ImGuiColumnsFlags_NoPreserveWidths
) && (column_index
< columns
->Count
-1);
13300 const float width
= preserve_width
? GetColumnWidthEx(columns
, column_index
, columns
->IsBeingResized
) : 0.0f
;
13302 if (!(columns
->Flags
& ImGuiColumnsFlags_NoForceWithinWindow
))
13303 offset
= ImMin(offset
, columns
->MaxX
- g
.Style
.ColumnsMinSpacing
* (columns
->Count
- column_index
));
13304 columns
->Columns
[column_index
].OffsetNorm
= PixelsToOffsetNorm(columns
, offset
- columns
->MinX
);
13306 if (preserve_width
)
13307 SetColumnOffset(column_index
+ 1, offset
+ ImMax(g
.Style
.ColumnsMinSpacing
, width
));
13310 void ImGui::SetColumnWidth(int column_index
, float width
)
13312 ImGuiWindow
* window
= GetCurrentWindowRead();
13313 ImGuiColumnsSet
* columns
= window
->DC
.ColumnsSet
;
13314 IM_ASSERT(columns
!= NULL
);
13316 if (column_index
< 0)
13317 column_index
= columns
->Current
;
13318 SetColumnOffset(column_index
+ 1, GetColumnOffset(column_index
) + width
);
13321 void ImGui::PushColumnClipRect(int column_index
)
13323 ImGuiWindow
* window
= GetCurrentWindowRead();
13324 ImGuiColumnsSet
* columns
= window
->DC
.ColumnsSet
;
13325 if (column_index
< 0)
13326 column_index
= columns
->Current
;
13328 PushClipRect(columns
->Columns
[column_index
].ClipRect
.Min
, columns
->Columns
[column_index
].ClipRect
.Max
, false);
13331 static ImGuiColumnsSet
* FindOrAddColumnsSet(ImGuiWindow
* window
, ImGuiID id
)
13333 for (int n
= 0; n
< window
->ColumnsStorage
.Size
; n
++)
13334 if (window
->ColumnsStorage
[n
].ID
== id
)
13335 return &window
->ColumnsStorage
[n
];
13337 window
->ColumnsStorage
.push_back(ImGuiColumnsSet());
13338 ImGuiColumnsSet
* columns
= &window
->ColumnsStorage
.back();
13343 void ImGui::BeginColumns(const char* str_id
, int columns_count
, ImGuiColumnsFlags flags
)
13345 ImGuiContext
& g
= *GImGui
;
13346 ImGuiWindow
* window
= GetCurrentWindow();
13348 IM_ASSERT(columns_count
> 1);
13349 IM_ASSERT(window
->DC
.ColumnsSet
== NULL
); // Nested columns are currently not supported
13351 // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
13352 // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
13353 PushID(0x11223347 + (str_id
? 0 : columns_count
));
13354 ImGuiID id
= window
->GetID(str_id
? str_id
: "columns");
13357 // Acquire storage for the columns set
13358 ImGuiColumnsSet
* columns
= FindOrAddColumnsSet(window
, id
);
13359 IM_ASSERT(columns
->ID
== id
);
13360 columns
->Current
= 0;
13361 columns
->Count
= columns_count
;
13362 columns
->Flags
= flags
;
13363 window
->DC
.ColumnsSet
= columns
;
13365 // Set state for first column
13366 const float content_region_width
= (window
->SizeContentsExplicit
.x
!= 0.0f
) ? (window
->SizeContentsExplicit
.x
) : (window
->InnerClipRect
.Max
.x
- window
->Pos
.x
);
13367 columns
->MinX
= window
->DC
.IndentX
- g
.Style
.ItemSpacing
.x
; // Lock our horizontal range
13368 columns
->MaxX
= ImMax(content_region_width
- window
->Scroll
.x
, columns
->MinX
+ 1.0f
);
13369 columns
->StartPosY
= window
->DC
.CursorPos
.y
;
13370 columns
->StartMaxPosX
= window
->DC
.CursorMaxPos
.x
;
13371 columns
->LineMinY
= columns
->LineMaxY
= window
->DC
.CursorPos
.y
;
13372 window
->DC
.ColumnsOffsetX
= 0.0f
;
13373 window
->DC
.CursorPos
.x
= (float)(int)(window
->Pos
.x
+ window
->DC
.IndentX
+ window
->DC
.ColumnsOffsetX
);
13375 // Clear data if columns count changed
13376 if (columns
->Columns
.Size
!= 0 && columns
->Columns
.Size
!= columns_count
+ 1)
13377 columns
->Columns
.resize(0);
13379 // Initialize defaults
13380 columns
->IsFirstFrame
= (columns
->Columns
.Size
== 0);
13381 if (columns
->Columns
.Size
== 0)
13383 columns
->Columns
.reserve(columns_count
+ 1);
13384 for (int n
= 0; n
< columns_count
+ 1; n
++)
13386 ImGuiColumnData column
;
13387 column
.OffsetNorm
= n
/ (float)columns_count
;
13388 columns
->Columns
.push_back(column
);
13392 for (int n
= 0; n
< columns_count
; n
++)
13394 // Compute clipping rectangle
13395 ImGuiColumnData
* column
= &columns
->Columns
[n
];
13396 float clip_x1
= ImFloor(0.5f
+ window
->Pos
.x
+ GetColumnOffset(n
) - 1.0f
);
13397 float clip_x2
= ImFloor(0.5f
+ window
->Pos
.x
+ GetColumnOffset(n
+ 1) - 1.0f
);
13398 column
->ClipRect
= ImRect(clip_x1
, -FLT_MAX
, clip_x2
, +FLT_MAX
);
13399 column
->ClipRect
.ClipWith(window
->ClipRect
);
13402 window
->DrawList
->ChannelsSplit(columns
->Count
);
13403 PushColumnClipRect();
13404 PushItemWidth(GetColumnWidth() * 0.65f
);
13407 void ImGui::EndColumns()
13409 ImGuiContext
& g
= *GImGui
;
13410 ImGuiWindow
* window
= GetCurrentWindow();
13411 ImGuiColumnsSet
* columns
= window
->DC
.ColumnsSet
;
13412 IM_ASSERT(columns
!= NULL
);
13416 window
->DrawList
->ChannelsMerge();
13418 columns
->LineMaxY
= ImMax(columns
->LineMaxY
, window
->DC
.CursorPos
.y
);
13419 window
->DC
.CursorPos
.y
= columns
->LineMaxY
;
13420 if (!(columns
->Flags
& ImGuiColumnsFlags_GrowParentContentsSize
))
13421 window
->DC
.CursorMaxPos
.x
= columns
->StartMaxPosX
; // Restore cursor max pos, as columns don't grow parent
13423 // Draw columns borders and handle resize
13424 bool is_being_resized
= false;
13425 if (!(columns
->Flags
& ImGuiColumnsFlags_NoBorder
) && !window
->SkipItems
)
13427 const float y1
= columns
->StartPosY
;
13428 const float y2
= window
->DC
.CursorPos
.y
;
13429 int dragging_column
= -1;
13430 for (int n
= 1; n
< columns
->Count
; n
++)
13432 float x
= window
->Pos
.x
+ GetColumnOffset(n
);
13433 const ImGuiID column_id
= columns
->ID
+ ImGuiID(n
);
13434 const float column_hw
= GetColumnsRectHalfWidth(); // Half-width for interaction
13435 const ImRect
column_rect(ImVec2(x
- column_hw
, y1
), ImVec2(x
+ column_hw
, y2
));
13436 KeepAliveID(column_id
);
13437 if (IsClippedEx(column_rect
, column_id
, false))
13440 bool hovered
= false, held
= false;
13441 if (!(columns
->Flags
& ImGuiColumnsFlags_NoResize
))
13443 ButtonBehavior(column_rect
, column_id
, &hovered
, &held
);
13444 if (hovered
|| held
)
13445 g
.MouseCursor
= ImGuiMouseCursor_ResizeEW
;
13446 if (held
&& !(columns
->Columns
[n
].Flags
& ImGuiColumnsFlags_NoResize
))
13447 dragging_column
= n
;
13450 // Draw column (we clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.)
13451 const ImU32 col
= GetColorU32(held
? ImGuiCol_SeparatorActive
: hovered
? ImGuiCol_SeparatorHovered
: ImGuiCol_Separator
);
13452 const float xi
= (float)(int)x
;
13453 window
->DrawList
->AddLine(ImVec2(xi
, ImMax(y1
+ 1.0f
, window
->ClipRect
.Min
.y
)), ImVec2(xi
, ImMin(y2
, window
->ClipRect
.Max
.y
)), col
);
13456 // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
13457 if (dragging_column
!= -1)
13459 if (!columns
->IsBeingResized
)
13460 for (int n
= 0; n
< columns
->Count
+ 1; n
++)
13461 columns
->Columns
[n
].OffsetNormBeforeResize
= columns
->Columns
[n
].OffsetNorm
;
13462 columns
->IsBeingResized
= is_being_resized
= true;
13463 float x
= GetDraggedColumnOffset(columns
, dragging_column
);
13464 SetColumnOffset(dragging_column
, x
);
13467 columns
->IsBeingResized
= is_being_resized
;
13469 window
->DC
.ColumnsSet
= NULL
;
13470 window
->DC
.ColumnsOffsetX
= 0.0f
;
13471 window
->DC
.CursorPos
.x
= (float)(int)(window
->Pos
.x
+ window
->DC
.IndentX
+ window
->DC
.ColumnsOffsetX
);
13474 // [2018-03: This is currently the only public API, while we are working on making BeginColumns/EndColumns user-facing]
13475 void ImGui::Columns(int columns_count
, const char* id
, bool border
)
13477 ImGuiWindow
* window
= GetCurrentWindow();
13478 IM_ASSERT(columns_count
>= 1);
13480 ImGuiColumnsFlags flags
= (border
? 0 : ImGuiColumnsFlags_NoBorder
);
13481 //flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior
13482 if (window
->DC
.ColumnsSet
!= NULL
&& window
->DC
.ColumnsSet
->Count
== columns_count
&& window
->DC
.ColumnsSet
->Flags
== flags
)
13485 if (window
->DC
.ColumnsSet
!= NULL
)
13488 if (columns_count
!= 1)
13489 BeginColumns(id
, columns_count
, flags
);
13492 void ImGui::Indent(float indent_w
)
13494 ImGuiContext
& g
= *GImGui
;
13495 ImGuiWindow
* window
= GetCurrentWindow();
13496 window
->DC
.IndentX
+= (indent_w
!= 0.0f
) ? indent_w
: g
.Style
.IndentSpacing
;
13497 window
->DC
.CursorPos
.x
= window
->Pos
.x
+ window
->DC
.IndentX
+ window
->DC
.ColumnsOffsetX
;
13500 void ImGui::Unindent(float indent_w
)
13502 ImGuiContext
& g
= *GImGui
;
13503 ImGuiWindow
* window
= GetCurrentWindow();
13504 window
->DC
.IndentX
-= (indent_w
!= 0.0f
) ? indent_w
: g
.Style
.IndentSpacing
;
13505 window
->DC
.CursorPos
.x
= window
->Pos
.x
+ window
->DC
.IndentX
+ window
->DC
.ColumnsOffsetX
;
13508 void ImGui::TreePush(const char* str_id
)
13510 ImGuiWindow
* window
= GetCurrentWindow();
13512 window
->DC
.TreeDepth
++;
13513 PushID(str_id
? str_id
: "#TreePush");
13516 void ImGui::TreePush(const void* ptr_id
)
13518 ImGuiWindow
* window
= GetCurrentWindow();
13520 window
->DC
.TreeDepth
++;
13521 PushID(ptr_id
? ptr_id
: (const void*)"#TreePush");
13524 void ImGui::TreePushRawID(ImGuiID id
)
13526 ImGuiWindow
* window
= GetCurrentWindow();
13528 window
->DC
.TreeDepth
++;
13529 window
->IDStack
.push_back(id
);
13532 void ImGui::TreePop()
13534 ImGuiContext
& g
= *GImGui
;
13535 ImGuiWindow
* window
= g
.CurrentWindow
;
13538 window
->DC
.TreeDepth
--;
13539 if (g
.NavMoveDir
== ImGuiDir_Left
&& g
.NavWindow
== window
&& NavMoveRequestButNoResultYet())
13540 if (g
.NavIdIsAlive
&& (window
->DC
.TreeDepthMayJumpToParentOnPop
& (1 << window
->DC
.TreeDepth
)))
13542 SetNavID(window
->IDStack
.back(), g
.NavLayer
);
13543 NavMoveRequestCancel();
13545 window
->DC
.TreeDepthMayJumpToParentOnPop
&= (1 << window
->DC
.TreeDepth
) - 1;
13547 IM_ASSERT(window
->IDStack
.Size
> 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much.
13551 void ImGui::Value(const char* prefix
, bool b
)
13553 Text("%s: %s", prefix
, (b
? "true" : "false"));
13556 void ImGui::Value(const char* prefix
, int v
)
13558 Text("%s: %d", prefix
, v
);
13561 void ImGui::Value(const char* prefix
, unsigned int v
)
13563 Text("%s: %d", prefix
, v
);
13566 void ImGui::Value(const char* prefix
, float v
, const char* float_format
)
13571 ImFormatString(fmt
, IM_ARRAYSIZE(fmt
), "%%s: %s", float_format
);
13572 Text(fmt
, prefix
, v
);
13576 Text("%s: %.3f", prefix
, v
);
13580 //-----------------------------------------------------------------------------
13582 //-----------------------------------------------------------------------------
13584 void ImGui::ClearDragDrop()
13586 ImGuiContext
& g
= *GImGui
;
13587 g
.DragDropActive
= false;
13588 g
.DragDropPayload
.Clear();
13589 g
.DragDropAcceptFlags
= 0;
13590 g
.DragDropAcceptIdCurr
= g
.DragDropAcceptIdPrev
= 0;
13591 g
.DragDropAcceptIdCurrRectSurface
= FLT_MAX
;
13592 g
.DragDropAcceptFrameCount
= -1;
13595 // Call when current ID is active.
13596 // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
13597 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags
)
13599 ImGuiContext
& g
= *GImGui
;
13600 ImGuiWindow
* window
= g
.CurrentWindow
;
13602 bool source_drag_active
= false;
13603 ImGuiID source_id
= 0;
13604 ImGuiID source_parent_id
= 0;
13605 int mouse_button
= 0;
13606 if (!(flags
& ImGuiDragDropFlags_SourceExtern
))
13608 source_id
= window
->DC
.LastItemId
;
13609 if (source_id
!= 0 && g
.ActiveId
!= source_id
) // Early out for most common case
13611 if (g
.IO
.MouseDown
[mouse_button
] == false)
13614 if (source_id
== 0)
13616 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
13617 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
13618 if (!(flags
& ImGuiDragDropFlags_SourceAllowNullID
))
13624 // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
13625 // We build a throwaway ID based on current ID stack + relative AABB of items in window.
13626 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
13627 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
13628 bool is_hovered
= (window
->DC
.LastItemStatusFlags
& ImGuiItemStatusFlags_HoveredRect
) != 0;
13629 if (!is_hovered
&& (g
.ActiveId
== 0 || g
.ActiveIdWindow
!= window
))
13631 source_id
= window
->DC
.LastItemId
= window
->GetIDFromRectangle(window
->DC
.LastItemRect
);
13633 SetHoveredID(source_id
);
13634 if (is_hovered
&& g
.IO
.MouseClicked
[mouse_button
])
13636 SetActiveID(source_id
, window
);
13637 FocusWindow(window
);
13639 if (g
.ActiveId
== source_id
) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
13640 g
.ActiveIdAllowOverlap
= is_hovered
;
13644 g
.ActiveIdAllowOverlap
= false;
13646 if (g
.ActiveId
!= source_id
)
13648 source_parent_id
= window
->IDStack
.back();
13649 source_drag_active
= IsMouseDragging(mouse_button
);
13654 source_id
= ImHash("#SourceExtern", 0);
13655 source_drag_active
= true;
13658 if (source_drag_active
)
13660 if (!g
.DragDropActive
)
13662 IM_ASSERT(source_id
!= 0);
13664 ImGuiPayload
& payload
= g
.DragDropPayload
;
13665 payload
.SourceId
= source_id
;
13666 payload
.SourceParentId
= source_parent_id
;
13667 g
.DragDropActive
= true;
13668 g
.DragDropSourceFlags
= flags
;
13669 g
.DragDropMouseButton
= mouse_button
;
13671 g
.DragDropWithinSourceOrTarget
= true;
13673 if (!(flags
& ImGuiDragDropFlags_SourceNoPreviewTooltip
))
13675 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
13676 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
13678 if (g
.DragDropActive
&& g
.DragDropAcceptIdPrev
&& (g
.DragDropAcceptFlags
& ImGuiDragDropFlags_AcceptNoPreviewTooltip
))
13680 ImGuiWindow
* tooltip_window
= g
.CurrentWindow
;
13681 tooltip_window
->SkipItems
= true;
13682 tooltip_window
->HiddenFramesRegular
= 1;
13686 if (!(flags
& ImGuiDragDropFlags_SourceNoDisableHover
) && !(flags
& ImGuiDragDropFlags_SourceExtern
))
13687 window
->DC
.LastItemStatusFlags
&= ~ImGuiItemStatusFlags_HoveredRect
;
13694 void ImGui::EndDragDropSource()
13696 ImGuiContext
& g
= *GImGui
;
13697 IM_ASSERT(g
.DragDropActive
);
13698 IM_ASSERT(g
.DragDropWithinSourceOrTarget
&& "Not after a BeginDragDropSource()?");
13700 if (!(g
.DragDropSourceFlags
& ImGuiDragDropFlags_SourceNoPreviewTooltip
))
13703 // Discard the drag if have not called SetDragDropPayload()
13704 if (g
.DragDropPayload
.DataFrameCount
== -1)
13706 g
.DragDropWithinSourceOrTarget
= false;
13709 // Use 'cond' to choose to submit payload on drag start or every frame
13710 bool ImGui::SetDragDropPayload(const char* type
, const void* data
, size_t data_size
, ImGuiCond cond
)
13712 ImGuiContext
& g
= *GImGui
;
13713 ImGuiPayload
& payload
= g
.DragDropPayload
;
13715 cond
= ImGuiCond_Always
;
13717 IM_ASSERT(type
!= NULL
);
13718 IM_ASSERT(strlen(type
) < IM_ARRAYSIZE(payload
.DataType
) && "Payload type can be at most 32 characters long");
13719 IM_ASSERT((data
!= NULL
&& data_size
> 0) || (data
== NULL
&& data_size
== 0));
13720 IM_ASSERT(cond
== ImGuiCond_Always
|| cond
== ImGuiCond_Once
);
13721 IM_ASSERT(payload
.SourceId
!= 0); // Not called between BeginDragDropSource() and EndDragDropSource()
13723 if (cond
== ImGuiCond_Always
|| payload
.DataFrameCount
== -1)
13726 ImStrncpy(payload
.DataType
, type
, IM_ARRAYSIZE(payload
.DataType
));
13727 g
.DragDropPayloadBufHeap
.resize(0);
13728 if (data_size
> sizeof(g
.DragDropPayloadBufLocal
))
13731 g
.DragDropPayloadBufHeap
.resize((int)data_size
);
13732 payload
.Data
= g
.DragDropPayloadBufHeap
.Data
;
13733 memcpy(payload
.Data
, data
, data_size
);
13735 else if (data_size
> 0)
13738 memset(&g
.DragDropPayloadBufLocal
, 0, sizeof(g
.DragDropPayloadBufLocal
));
13739 payload
.Data
= g
.DragDropPayloadBufLocal
;
13740 memcpy(payload
.Data
, data
, data_size
);
13744 payload
.Data
= NULL
;
13746 payload
.DataSize
= (int)data_size
;
13748 payload
.DataFrameCount
= g
.FrameCount
;
13750 return (g
.DragDropAcceptFrameCount
== g
.FrameCount
) || (g
.DragDropAcceptFrameCount
== g
.FrameCount
- 1);
13753 bool ImGui::BeginDragDropTargetCustom(const ImRect
& bb
, ImGuiID id
)
13755 ImGuiContext
& g
= *GImGui
;
13756 if (!g
.DragDropActive
)
13759 ImGuiWindow
* window
= g
.CurrentWindow
;
13760 if (g
.HoveredWindow
== NULL
|| window
->RootWindow
!= g
.HoveredWindow
->RootWindow
)
13762 IM_ASSERT(id
!= 0);
13763 if (!IsMouseHoveringRect(bb
.Min
, bb
.Max
) || (id
== g
.DragDropPayload
.SourceId
))
13766 IM_ASSERT(g
.DragDropWithinSourceOrTarget
== false);
13767 g
.DragDropTargetRect
= bb
;
13768 g
.DragDropTargetId
= id
;
13769 g
.DragDropWithinSourceOrTarget
= true;
13773 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
13774 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
13775 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
13776 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
13777 bool ImGui::BeginDragDropTarget()
13779 ImGuiContext
& g
= *GImGui
;
13780 if (!g
.DragDropActive
)
13783 ImGuiWindow
* window
= g
.CurrentWindow
;
13784 if (!(window
->DC
.LastItemStatusFlags
& ImGuiItemStatusFlags_HoveredRect
))
13786 if (g
.HoveredWindow
== NULL
|| window
->RootWindow
!= g
.HoveredWindow
->RootWindow
)
13789 const ImRect
& display_rect
= (window
->DC
.LastItemStatusFlags
& ImGuiItemStatusFlags_HasDisplayRect
) ? window
->DC
.LastItemDisplayRect
: window
->DC
.LastItemRect
;
13790 ImGuiID id
= window
->DC
.LastItemId
;
13792 id
= window
->GetIDFromRectangle(display_rect
);
13793 if (g
.DragDropPayload
.SourceId
== id
)
13796 IM_ASSERT(g
.DragDropWithinSourceOrTarget
== false);
13797 g
.DragDropTargetRect
= display_rect
;
13798 g
.DragDropTargetId
= id
;
13799 g
.DragDropWithinSourceOrTarget
= true;
13803 bool ImGui::IsDragDropPayloadBeingAccepted()
13805 ImGuiContext
& g
= *GImGui
;
13806 return g
.DragDropActive
&& g
.DragDropAcceptIdPrev
!= 0;
13809 const ImGuiPayload
* ImGui::AcceptDragDropPayload(const char* type
, ImGuiDragDropFlags flags
)
13811 ImGuiContext
& g
= *GImGui
;
13812 ImGuiWindow
* window
= g
.CurrentWindow
;
13813 ImGuiPayload
& payload
= g
.DragDropPayload
;
13814 IM_ASSERT(g
.DragDropActive
); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
13815 IM_ASSERT(payload
.DataFrameCount
!= -1); // Forgot to call EndDragDropTarget() ?
13816 if (type
!= NULL
&& !payload
.IsDataType(type
))
13819 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
13820 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
13821 const bool was_accepted_previously
= (g
.DragDropAcceptIdPrev
== g
.DragDropTargetId
);
13822 ImRect r
= g
.DragDropTargetRect
;
13823 float r_surface
= r
.GetWidth() * r
.GetHeight();
13824 if (r_surface
< g
.DragDropAcceptIdCurrRectSurface
)
13826 g
.DragDropAcceptFlags
= flags
;
13827 g
.DragDropAcceptIdCurr
= g
.DragDropTargetId
;
13828 g
.DragDropAcceptIdCurrRectSurface
= r_surface
;
13831 // Render default drop visuals
13832 payload
.Preview
= was_accepted_previously
;
13833 flags
|= (g
.DragDropSourceFlags
& ImGuiDragDropFlags_AcceptNoDrawDefaultRect
); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
13834 if (!(flags
& ImGuiDragDropFlags_AcceptNoDrawDefaultRect
) && payload
.Preview
)
13836 // FIXME-DRAG: Settle on a proper default visuals for drop target.
13838 bool push_clip_rect
= !window
->ClipRect
.Contains(r
);
13839 if (push_clip_rect
) window
->DrawList
->PushClipRect(r
.Min
-ImVec2(1,1), r
.Max
+ImVec2(1,1));
13840 window
->DrawList
->AddRect(r
.Min
, r
.Max
, GetColorU32(ImGuiCol_DragDropTarget
), 0.0f
, ~0, 2.0f
);
13841 if (push_clip_rect
) window
->DrawList
->PopClipRect();
13844 g
.DragDropAcceptFrameCount
= g
.FrameCount
;
13845 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()
13846 if (!payload
.Delivery
&& !(flags
& ImGuiDragDropFlags_AcceptBeforeDelivery
))
13852 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
13853 void ImGui::EndDragDropTarget()
13855 ImGuiContext
& g
= *GImGui
;
13856 IM_ASSERT(g
.DragDropActive
);
13857 IM_ASSERT(g
.DragDropWithinSourceOrTarget
);
13858 g
.DragDropWithinSourceOrTarget
= false;
13861 //-----------------------------------------------------------------------------
13862 // PLATFORM DEPENDENT HELPERS
13863 //-----------------------------------------------------------------------------
13865 #if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
13866 #undef WIN32_LEAN_AND_MEAN
13867 #define WIN32_LEAN_AND_MEAN
13868 #ifndef __MINGW32__
13869 #include <Windows.h>
13871 #include <windows.h>
13875 // Win32 API clipboard implementation
13876 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
13879 #pragma comment(lib, "user32")
13882 static const char* GetClipboardTextFn_DefaultImpl(void*)
13884 static ImVector
<char> buf_local
;
13886 if (!::OpenClipboard(NULL
))
13888 HANDLE wbuf_handle
= ::GetClipboardData(CF_UNICODETEXT
);
13889 if (wbuf_handle
== NULL
)
13891 ::CloseClipboard();
13894 if (ImWchar
* wbuf_global
= (ImWchar
*)::GlobalLock(wbuf_handle
))
13896 int buf_len
= ImTextCountUtf8BytesFromStr(wbuf_global
, NULL
) + 1;
13897 buf_local
.resize(buf_len
);
13898 ImTextStrToUtf8(buf_local
.Data
, buf_len
, wbuf_global
, NULL
);
13900 ::GlobalUnlock(wbuf_handle
);
13901 ::CloseClipboard();
13902 return buf_local
.Data
;
13905 static void SetClipboardTextFn_DefaultImpl(void*, const char* text
)
13907 if (!::OpenClipboard(NULL
))
13909 const int wbuf_length
= ImTextCountCharsFromUtf8(text
, NULL
) + 1;
13910 HGLOBAL wbuf_handle
= ::GlobalAlloc(GMEM_MOVEABLE
, (SIZE_T
)wbuf_length
* sizeof(ImWchar
));
13911 if (wbuf_handle
== NULL
)
13913 ::CloseClipboard();
13916 ImWchar
* wbuf_global
= (ImWchar
*)::GlobalLock(wbuf_handle
);
13917 ImTextStrFromUtf8(wbuf_global
, wbuf_length
, text
, NULL
);
13918 ::GlobalUnlock(wbuf_handle
);
13919 ::EmptyClipboard();
13920 if (::SetClipboardData(CF_UNICODETEXT
, wbuf_handle
) == NULL
)
13921 ::GlobalFree(wbuf_handle
);
13922 ::CloseClipboard();
13927 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
13928 static const char* GetClipboardTextFn_DefaultImpl(void*)
13930 ImGuiContext
& g
= *GImGui
;
13931 return g
.PrivateClipboard
.empty() ? NULL
: g
.PrivateClipboard
.begin();
13934 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
13935 static void SetClipboardTextFn_DefaultImpl(void*, const char* text
)
13937 ImGuiContext
& g
= *GImGui
;
13938 g
.PrivateClipboard
.clear();
13939 const char* text_end
= text
+ strlen(text
);
13940 g
.PrivateClipboard
.resize((int)(text_end
- text
) + 1);
13941 memcpy(&g
.PrivateClipboard
[0], text
, (size_t)(text_end
- text
));
13942 g
.PrivateClipboard
[(int)(text_end
- text
)] = 0;
13947 // Win32 API IME support (for Asian languages, etc.)
13948 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
13952 #pragma comment(lib, "imm32")
13955 static void ImeSetInputScreenPosFn_DefaultImpl(int x
, int y
)
13957 // Notify OS Input Method Editor of text input position
13958 if (HWND hwnd
= (HWND
)GImGui
->IO
.ImeWindowHandle
)
13959 if (HIMC himc
= ::ImmGetContext(hwnd
))
13961 COMPOSITIONFORM cf
;
13962 cf
.ptCurrentPos
.x
= x
;
13963 cf
.ptCurrentPos
.y
= y
;
13964 cf
.dwStyle
= CFS_FORCE_POSITION
;
13965 ::ImmSetCompositionWindow(himc
, &cf
);
13966 ::ImmReleaseContext(hwnd
, himc
);
13972 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
13976 //-----------------------------------------------------------------------------
13978 //-----------------------------------------------------------------------------
13980 void ImGui::ShowMetricsWindow(bool* p_open
)
13982 if (ImGui::Begin("ImGui Metrics", p_open
))
13984 static bool show_draw_cmd_clip_rects
= true;
13985 static bool show_window_begin_order
= false;
13986 ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
13987 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f
/ ImGui::GetIO().Framerate
, ImGui::GetIO().Framerate
);
13988 ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices
, ImGui::GetIO().MetricsRenderIndices
, ImGui::GetIO().MetricsRenderIndices
/ 3);
13989 ImGui::Text("%d allocations", (int)GImAllocatorActiveAllocationsCount
);
13990 ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_draw_cmd_clip_rects
);
13991 ImGui::Checkbox("Ctrl shows window begin order", &show_window_begin_order
);
13993 ImGui::Separator();
13997 static void NodeDrawList(ImGuiWindow
* window
, ImDrawList
* draw_list
, const char* label
)
13999 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
);
14000 if (draw_list
== ImGui::GetWindowDrawList())
14003 ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
14004 if (node_open
) ImGui::TreePop();
14008 ImDrawList
* overlay_draw_list
= GetOverlayDrawList(); // Render additional visuals into the top-most draw list
14009 if (window
&& IsItemHovered())
14010 overlay_draw_list
->AddRect(window
->Pos
, window
->Pos
+ window
->Size
, IM_COL32(255, 255, 0, 255));
14014 int elem_offset
= 0;
14015 for (const ImDrawCmd
* pcmd
= draw_list
->CmdBuffer
.begin(); pcmd
< draw_list
->CmdBuffer
.end(); elem_offset
+= pcmd
->ElemCount
, pcmd
++)
14017 if (pcmd
->UserCallback
== NULL
&& pcmd
->ElemCount
== 0)
14019 if (pcmd
->UserCallback
)
14021 ImGui::BulletText("Callback %p, user_data %p", pcmd
->UserCallback
, pcmd
->UserCallbackData
);
14024 ImDrawIdx
* idx_buffer
= (draw_list
->IdxBuffer
.Size
> 0) ? draw_list
->IdxBuffer
.Data
: NULL
;
14025 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
);
14026 if (show_draw_cmd_clip_rects
&& ImGui::IsItemHovered())
14028 ImRect clip_rect
= pcmd
->ClipRect
;
14030 for (int i
= elem_offset
; i
< elem_offset
+ (int)pcmd
->ElemCount
; i
++)
14031 vtxs_rect
.Add(draw_list
->VtxBuffer
[idx_buffer
? idx_buffer
[i
] : i
].pos
);
14032 clip_rect
.Floor(); overlay_draw_list
->AddRect(clip_rect
.Min
, clip_rect
.Max
, IM_COL32(255,255,0,255));
14033 vtxs_rect
.Floor(); overlay_draw_list
->AddRect(vtxs_rect
.Min
, vtxs_rect
.Max
, IM_COL32(255,0,255,255));
14035 if (!pcmd_node_open
)
14038 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
14039 ImGuiListClipper
clipper(pcmd
->ElemCount
/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
14040 while (clipper
.Step())
14041 for (int prim
= clipper
.DisplayStart
, vtx_i
= elem_offset
+ clipper
.DisplayStart
*3; prim
< clipper
.DisplayEnd
; prim
++)
14044 char *buf_p
= buf
, *buf_end
= buf
+ IM_ARRAYSIZE(buf
);
14045 ImVec2 triangles_pos
[3];
14046 for (int n
= 0; n
< 3; n
++, vtx_i
++)
14048 ImDrawVert
& v
= draw_list
->VtxBuffer
[idx_buffer
? idx_buffer
[vtx_i
] : vtx_i
];
14049 triangles_pos
[n
] = v
.pos
;
14050 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
);
14052 ImGui::Selectable(buf
, false);
14053 if (ImGui::IsItemHovered())
14055 ImDrawListFlags backup_flags
= overlay_draw_list
->Flags
;
14056 overlay_draw_list
->Flags
&= ~ImDrawListFlags_AntiAliasedLines
; // Disable AA on triangle outlines at is more readable for very large and thin triangles.
14057 overlay_draw_list
->AddPolyline(triangles_pos
, 3, IM_COL32(255,255,0,255), true, 1.0f
);
14058 overlay_draw_list
->Flags
= backup_flags
;
14066 static void NodeWindows(ImVector
<ImGuiWindow
*>& windows
, const char* label
)
14068 if (!ImGui::TreeNode(label
, "%s (%d)", label
, windows
.Size
))
14070 for (int i
= 0; i
< windows
.Size
; i
++)
14071 Funcs::NodeWindow(windows
[i
], "Window");
14075 static void NodeWindow(ImGuiWindow
* window
, const char* label
)
14077 if (!ImGui::TreeNode(window
, "%s '%s', %d @ 0x%p", label
, window
->Name
, window
->Active
|| window
->WasActive
, window
))
14079 ImGuiWindowFlags flags
= window
->Flags
;
14080 NodeDrawList(window
, window
->DrawList
, "DrawList");
14081 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
);
14082 ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s..)", flags
,
14083 (flags
& ImGuiWindowFlags_ChildWindow
) ? "Child " : "", (flags
& ImGuiWindowFlags_Tooltip
) ? "Tooltip " : "", (flags
& ImGuiWindowFlags_Popup
) ? "Popup " : "",
14084 (flags
& ImGuiWindowFlags_Modal
) ? "Modal " : "", (flags
& ImGuiWindowFlags_ChildMenu
) ? "ChildMenu " : "", (flags
& ImGuiWindowFlags_NoSavedSettings
) ? "NoSavedSettings " : "",
14085 (flags
& ImGuiWindowFlags_NoInputs
) ? "NoInputs":"", (flags
& ImGuiWindowFlags_AlwaysAutoResize
) ? "AlwaysAutoResize" : "");
14086 ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window
->Scroll
.x
, GetScrollMaxX(window
), window
->Scroll
.y
, GetScrollMaxY(window
));
14087 ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window
->Active
, window
->WasActive
, window
->WriteAccessed
, (window
->Active
|| window
->WasActive
) ? window
->BeginOrderWithinContext
: -1);
14088 ImGui::BulletText("Appearing: %d, Hidden: %d (Reg %d Resize %d), SkipItems: %d", window
->Appearing
, window
->Hidden
, window
->HiddenFramesRegular
, window
->HiddenFramesForResize
, window
->SkipItems
);
14089 ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window
->NavLastIds
[0], window
->NavLastIds
[1], window
->DC
.NavLayerActiveMask
);
14090 ImGui::BulletText("NavLastChildNavWindow: %s", window
->NavLastChildNavWindow
? window
->NavLastChildNavWindow
->Name
: "NULL");
14091 if (!window
->NavRectRel
[0].IsInverted())
14092 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
);
14094 ImGui::BulletText("NavRectRel[0]: <None>");
14095 if (window
->RootWindow
!= window
) NodeWindow(window
->RootWindow
, "RootWindow");
14096 if (window
->ParentWindow
!= NULL
) NodeWindow(window
->ParentWindow
, "ParentWindow");
14097 if (window
->DC
.ChildWindows
.Size
> 0) NodeWindows(window
->DC
.ChildWindows
, "ChildWindows");
14098 if (window
->ColumnsStorage
.Size
> 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window
->ColumnsStorage
.Size
))
14100 for (int n
= 0; n
< window
->ColumnsStorage
.Size
; n
++)
14102 const ImGuiColumnsSet
* columns
= &window
->ColumnsStorage
[n
];
14103 if (ImGui::TreeNode((void*)(uintptr_t)columns
->ID
, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns
->ID
, columns
->Count
, columns
->Flags
))
14105 ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns
->MaxX
- columns
->MinX
, columns
->MinX
, columns
->MaxX
);
14106 for (int column_n
= 0; column_n
< columns
->Columns
.Size
; column_n
++)
14107 ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n
, columns
->Columns
[column_n
].OffsetNorm
, OffsetNormToPixels(columns
, columns
->Columns
[column_n
].OffsetNorm
));
14113 ImGui::BulletText("Storage: %d bytes", window
->StateStorage
.Data
.Size
* (int)sizeof(ImGuiStorage::Pair
));
14118 // Access private state, we are going to display the draw lists from last frame
14119 ImGuiContext
& g
= *GImGui
;
14120 Funcs::NodeWindows(g
.Windows
, "Windows");
14121 if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g
.DrawDataBuilder
.Layers
[0].Size
))
14123 for (int i
= 0; i
< g
.DrawDataBuilder
.Layers
[0].Size
; i
++)
14124 Funcs::NodeDrawList(NULL
, g
.DrawDataBuilder
.Layers
[0][i
], "DrawList");
14127 if (ImGui::TreeNode("Popups", "Popups (%d)", g
.OpenPopupStack
.Size
))
14129 for (int i
= 0; i
< g
.OpenPopupStack
.Size
; i
++)
14131 ImGuiWindow
* window
= g
.OpenPopupStack
[i
].Window
;
14132 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" : "");
14136 if (ImGui::TreeNode("Internal state"))
14138 const char* input_source_names
[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names
) == ImGuiInputSource_COUNT
);
14139 ImGui::Text("HoveredWindow: '%s'", g
.HoveredWindow
? g
.HoveredWindow
->Name
: "NULL");
14140 ImGui::Text("HoveredRootWindow: '%s'", g
.HoveredRootWindow
? g
.HoveredRootWindow
->Name
: "NULL");
14141 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
14142 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
]);
14143 ImGui::Text("ActiveIdWindow: '%s'", g
.ActiveIdWindow
? g
.ActiveIdWindow
->Name
: "NULL");
14144 ImGui::Text("MovingWindow: '%s'", g
.MovingWindow
? g
.MovingWindow
->Name
: "NULL");
14145 ImGui::Text("NavWindow: '%s'", g
.NavWindow
? g
.NavWindow
->Name
: "NULL");
14146 ImGui::Text("NavId: 0x%08X, NavLayer: %d", g
.NavId
, g
.NavLayer
);
14147 ImGui::Text("NavInputSource: %s", input_source_names
[g
.NavInputSource
]);
14148 ImGui::Text("NavActive: %d, NavVisible: %d", g
.IO
.NavActive
, g
.IO
.NavVisible
);
14149 ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g
.NavActivateId
, g
.NavInputId
);
14150 ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g
.NavDisableHighlight
, g
.NavDisableMouseHover
);
14151 ImGui::Text("NavWindowingTarget: '%s'", g
.NavWindowingTarget
? g
.NavWindowingTarget
->Name
: "NULL");
14152 ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g
.DragDropActive
, g
.DragDropPayload
.SourceId
, g
.DragDropPayload
.DataType
, g
.DragDropPayload
.DataSize
);
14157 if (g
.IO
.KeyCtrl
&& show_window_begin_order
)
14159 for (int n
= 0; n
< g
.Windows
.Size
; n
++)
14161 ImGuiWindow
* window
= g
.Windows
[n
];
14162 if ((window
->Flags
& ImGuiWindowFlags_ChildWindow
) || !window
->WasActive
)
14165 ImFormatString(buf
, IM_ARRAYSIZE(buf
), "%d", window
->BeginOrderWithinContext
);
14166 float font_size
= ImGui::GetFontSize() * 2;
14167 ImDrawList
* overlay_draw_list
= GetOverlayDrawList();
14168 overlay_draw_list
->AddRectFilled(window
->Pos
, window
->Pos
+ ImVec2(font_size
, font_size
), IM_COL32(200, 100, 100, 255));
14169 overlay_draw_list
->AddText(NULL
, font_size
, window
->Pos
, IM_COL32(255, 255, 255, 255), buf
);
14176 //-----------------------------------------------------------------------------
14178 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
14179 // Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github.
14180 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
14181 #include "imgui_user.inl"
14184 //-----------------------------------------------------------------------------