Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/wolfpld/tracy.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBartosz Taudul <wolf.pld@gmail.com>2017-11-10 05:01:56 +0300
committerBartosz Taudul <wolf.pld@gmail.com>2017-11-10 05:01:56 +0300
commit97fad073d942ab06083d65b5961e583bc033d528 (patch)
tree14f98e89f2497c11c5516a7c0b288627b1a24c9f /imgui/imgui.cpp
parentfbfe625828dcd79e0b6557940181b8babe941ee8 (diff)
Update imgui to 1.52.
Diffstat (limited to 'imgui/imgui.cpp')
-rw-r--r--imgui/imgui.cpp2141
1 files changed, 1267 insertions, 874 deletions
diff --git a/imgui/imgui.cpp b/imgui/imgui.cpp
index 26ed1826..b15c059f 100644
--- a/imgui/imgui.cpp
+++ b/imgui/imgui.cpp
@@ -1,11 +1,11 @@
-// dear imgui, v1.51
+// dear imgui, v1.52
// (main code and documentation)
// See ImGui::ShowTestWindow() in imgui_demo.cpp for demo code.
-// Newcomers, read 'Programmer guide' below for notes on how to setup ImGui in your codebase.
+// Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
// Get latest version at https://github.com/ocornut/imgui
// Releases change-log at https://github.com/ocornut/imgui/releases
-// Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/772
+// Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/1269
// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
// This library is free but I need your support to sustain development and maintenance.
// If you work for a company, please consider financial support, e.g: https://www.patreon.com/imgui
@@ -17,22 +17,22 @@
- END-USER GUIDE
- PROGRAMMER GUIDE (read me!)
- Read first
- - How to update to a newer version of ImGui
- - Getting started with integrating ImGui in your code/engine
+ - How to update to a newer version of Dear ImGui
+ - Getting started with integrating Dear ImGui in your code/engine
- API BREAKING CHANGES (read me when you update!)
- ISSUES & TODO LIST
- FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
- How can I help?
- What is ImTextureID and how do I display an image?
- - I integrated ImGui in my engine and the text or lines are blurry..
- - I integrated ImGui in my engine and some elements are clipping or disappearing when I move windows around..
+ - I integrated Dear ImGui in my engine and the text or lines are blurry..
+ - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
- How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on labels/IDs.
- - How can I tell when ImGui wants my mouse/keyboard inputs VS when I can pass them to my application?
+ - How can I tell when Dear ImGui wants my mouse/keyboard inputs VS when I can pass them to my application?
- How can I load a different font than the default?
- How can I easily use icons in my application?
- How can I load multiple fonts?
- How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
- - How can I preserve my ImGui context across reloading a DLL? (loss of the global/static variables)
+ - How can I preserve my Dear ImGui context across reloading a DLL? (loss of the global/static variables)
- How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
- ISSUES & TODO-LIST
- CODE
@@ -48,7 +48,8 @@
- Minimize setup and maintenance
- Minimize state storage on user side
- Portable, minimize dependencies, run on target (consoles, phones, etc.)
- - Efficient runtime and memory consumption (NB- we do allocate when "growing" content - creating a window / opening a tree node for the first time, etc. - but a typical frame won't allocate anything)
+ - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window, opening a tree node
+ for the first time, etc. but a typical frame won't allocate anything)
Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
- Doesn't look fancy, doesn't animate
@@ -85,11 +86,12 @@
READ FIRST
- Read the FAQ below this section!
- - Your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention on your side, no state duplication, less sync, less bugs.
+ - Your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention
+ on your side, no state duplication, less sync, less bugs.
- Call and read ImGui::ShowTestWindow() for demo code demonstrating most features.
- You can learn about immediate-mode gui principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861
- HOW TO UPDATE TO A NEWER VERSION OF IMGUI
+ HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
- Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
- Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
@@ -98,27 +100,30 @@
Please report any issue to the GitHub page!
- Try to keep your copy of dear imgui reasonably up to date.
- GETTING STARTED WITH INTEGRATING IMGUI IN YOUR CODE/ENGINE
+ GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
- - Add the ImGui source files to your projects, using your preferred build system. It is recommended you build the .cpp files as part of your project and not as a library.
+ - Add the Dear ImGui source files to your projects, using your preferred build system.
+ It is recommended you build the .cpp files as part of your project and not as a library.
- You can later customize the imconfig.h file to tweak some compilation time behavior, such as integrating imgui types with your own maths types.
- - See examples/ folder for standalone sample applications. To understand the integration process, you can read examples/opengl2_example/ because it is short,
- then switch to the one more appropriate to your use case.
+ - See examples/ folder for standalone sample applications. To understand the integration process, you can read examples/opengl2_example/ because
+ it is short, then switch to the one more appropriate to your use case.
- You may be able to grab and copy a ready made imgui_impl_*** file from the examples/.
- - When using ImGui, your programming IDE if your friend: follow the declaration of variables, functions and types to find comments about them.
+ - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
- - Init: retrieve the ImGuiIO structure with ImGui::GetIO() and fill the fields marked 'Settings': at minimum you need to set io.DisplaySize (application resolution).
- Later on you will fill your keyboard mapping, clipboard handlers, and other advanced features but for a basic integration you don't need to worry about it all.
+ - Init: retrieve the ImGuiIO structure with ImGui::GetIO() and fill the fields marked 'Settings': at minimum you need to set io.DisplaySize
+ (application resolution). Later on you will fill your keyboard mapping, clipboard handlers, and other advanced features but for a basic
+ integration you don't need to worry about it all.
- Init: call io.Fonts->GetTexDataAsRGBA32(...), it will build the font atlas texture, then load the texture pixels into graphics memory.
- Every frame:
- In your main loop as early a possible, fill the IO fields marked 'Input' (e.g. mouse position, buttons, keyboard info, etc.)
- - Call ImGui::NewFrame() to begin the imgui frame
+ - Call ImGui::NewFrame() to begin the frame
- You can use any ImGui function you want between NewFrame() and Render()
- Call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your io.RenderDrawListFn handler.
- (if you don't need to render, you still need to call Render() and ignore the callback, or call EndFrame() instead. if you call neither some aspects of windows focusing/moving will appear broken.)
+ (Even if you don't render, call Render() and ignore the callback, or call EndFrame() instead. Otherwhise some features will break)
- All rendering information are stored into command-lists until ImGui::Render() is called.
- - ImGui never touches or knows about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide.
- - Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases of your own application.
+ - Dear ImGui never touches or knows about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide.
+ - Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases
+ of your own application.
- Refer to the examples applications in the examples/ folder for instruction on how to setup your code.
- A minimal application skeleton may be:
@@ -126,7 +131,7 @@
ImGuiIO& io = ImGui::GetIO();
io.DisplaySize.x = 1920.0f;
io.DisplaySize.y = 1280.0f;
- io.RenderDrawListsFn = MyRenderFunction; // Setup a render function, or set to NULL and call GetDrawData() after Render() to access the render data.
+ io.RenderDrawListsFn = MyRenderFunction; // Setup a render function, or set to NULL and call GetDrawData() after Render() to access render data.
// TODO: Fill others settings of the io structure later.
// Load texture atlas (there is a default font so you don't need to care about choosing a font yet)
@@ -180,9 +185,16 @@
}
else
{
- // Render 'pcmd->ElemCount/3' texture triangles
+ // The texture for the draw call is specified by pcmd->TextureId.
+ // The vast majority of draw calls with use the imgui texture atlas, which value you have set yourself during initialization.
MyEngineBindTexture(pcmd->TextureId);
+
+ // We are using scissoring to clip some objects. All low-level graphics API supports it.
+ // If your engine doesn't support scissoring yet, you will get some small glitches (some elements outside their bounds) which you can fix later.
MyEngineScissor((int)pcmd->ClipRect.x, (int)pcmd->ClipRect.y, (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y));
+
+ // Render 'pcmd->ElemCount/3' indexed triangles.
+ // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits if your engine doesn't support 16-bits indices.
MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
}
idx_buffer += pcmd->ElemCount;
@@ -204,7 +216,15 @@
Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code.
Also read releases logs https://github.com/ocornut/imgui/releases for more details.
- - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete).
+ - 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.
+ - 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.
+ - 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.
+ removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
+ - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
+ - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
+ - 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).
+ - 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)".
+ - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
- renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
- renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
- 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
@@ -337,40 +357,41 @@
======================================
Q: How can I help?
- A: - If you are experienced enough with ImGui and with C/C++, look at the todo list and see how you want/can help!
+ A: - If you are experienced enough with Dear ImGui and with C/C++, look at the todo list and see how you want/can help!
- Become a Patron/donate! Convince your company to become a Patron or provide serious funding for development time! See http://www.patreon.com/imgui
Q: What is ImTextureID and how do I display an image?
A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function.
- ImGui knows nothing about what those bits represent, it just passes them around. It is up to you to decide what you want the void* to carry!
+ Dear ImGui knows nothing about what those bits represent, it just passes them around. It is up to you to decide what you want the void* to carry!
It could be an identifier to your OpenGL texture (cast GLuint to void*), a pointer to your custom engine material (cast MyMaterial* to void*), etc.
At the end of the chain, your renderer takes this void* to cast it back into whatever it needs to select a current texture to render.
Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing.
(c++ tip: OpenGL uses integers to identify textures. You can safely store an integer into a void*, just cast it to void*, don't take it's address!)
To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions.
- ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use.
+ Dear ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use.
It is your responsibility to get textures uploaded to your GPU.
- Q: I integrated ImGui in my engine and the text or lines are blurry..
+ Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
- Q: I integrated ImGui in my engine and some elements are clipping or disappearing when I move windows around..
- A: Most likely you are mishandling the clipping rectangles in your render function. Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
+ Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
+ A: You are probably mishandling the clipping rectangles in your render function.
+ Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
Q: Can I have multiple widgets with the same label? Can I have widget without a label?
- A: Yes. A primer on the use of labels/IDs in ImGui..
+ A: Yes. A primer on the use of labels/IDs in Dear ImGui..
- Elements that are not clickable, such as Text() items don't need an ID.
- - Interactive widgets require state to be carried over multiple frames (most typically ImGui often needs to remember what is the "active" widget).
- to do so they need a unique ID. unique ID are typically derived from a string label, an integer index or a pointer.
+ - Interactive widgets require state to be carried over multiple frames (most typically Dear ImGui often needs to remember what is
+ the "active" widget). to do so they need a unique ID. unique ID are typically derived from a string label, an integer index or a pointer.
Button("OK"); // Label = "OK", ID = hash of "OK"
Button("Cancel"); // Label = "Cancel", ID = hash of "Cancel"
- - ID are uniquely scoped within windows, tree nodes, etc. so no conflict can happen if you have two buttons called "OK" in two different windows
- or in two different locations of a tree.
+ - ID are uniquely scoped within windows, tree nodes, etc. so no conflict can happen if you have two buttons called "OK"
+ in two different windows or in two different locations of a tree.
- If you have a same ID twice in the same location, you'll have a conflict:
@@ -379,8 +400,8 @@
Fear not! this is easy to solve and there are many ways to solve it!
- - When passing a label you can optionally specify extra unique ID information within string itself. This helps solving the simpler collision cases.
- use "##" to pass a complement to the ID that won't be visible to the end-user:
+ - When passing a label you can optionally specify extra unique ID information within string itself.
+ This helps solving the simpler collision cases. Use "##" to pass a complement to the ID that won't be visible to the end-user:
Button("Play"); // Label = "Play", ID = hash of "Play"
Button("Play##foo1"); // Label = "Play", ID = hash of "Play##foo1" (different from above)
@@ -448,18 +469,22 @@
- When working with trees, ID are used to preserve the open/close state of each tree node.
Depending on your use cases you may want to use strings, indices or pointers as ID.
- e.g. when displaying a single object that may change over time (1-1 relationship), using a static string as ID will preserve your node open/closed state when the targeted object change.
- e.g. when displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state differently. experiment and see what makes more sense!
+ e.g. when displaying a single object that may change over time (dynamic 1-1 relationship), using a static string as ID will preserve your
+ node open/closed state when the targeted object change.
+ e.g. when displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state differently.
+ experiment and see what makes more sense!
- Q: How can I tell when ImGui wants my mouse/keyboard inputs VS when I can pass them to my application?
+ Q: How can I tell when Dear ImGui wants my mouse/keyboard inputs VS when I can pass them to my application?
A: You can read the 'io.WantCaptureMouse'/'io.WantCaptureKeyboard'/'ioWantTextInput' flags from the ImGuiIO structure.
- When 'io.WantCaptureMouse' or 'io.WantCaptureKeyboard' flags are set you may want to discard/hide the inputs from the rest of your application.
- - 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 without a keyboard).
- Preferably read the flags after calling ImGui::NewFrame() to avoid them lagging by one frame. But reading those flags before calling NewFrame() is also generally ok,
- as the bool toggles fairly rarely and you don't generally expect to interact with either ImGui or your application during the same frame when that transition occurs.
- ImGui is tracking dragging and widget activity that may occur outside the boundary of a window, so 'io.WantCaptureMouse' is more accurate and correct than checking if a window is hovered.
- (Advanced note: text input releases focus on Return 'KeyDown', so the following Return 'KeyUp' event that your application receive will typically have 'io.WantCaptureKeyboard=false'.
- Depending on your application logic it may or not be inconvenient. You might want to track which key-downs were for ImGui (e.g. with an array of bool) and filter out the corresponding key-ups.)
+ - 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).
+ Preferably read the flags after calling ImGui::NewFrame() to avoid them lagging by one frame. But reading those flags before calling NewFrame() is
+ also generally ok, as the bool toggles fairly rarely and you don't generally expect to interact with either Dear ImGui or your application during
+ the same frame when that transition occurs. Dear ImGui is tracking dragging and widget activity that may occur outside the boundary of a window,
+ so 'io.WantCaptureMouse' is more accurate and correct than checking if a window is hovered.
+ (Advanced note: text input releases focus on Return 'KeyDown', so the following Return 'KeyUp' event that your application receive will typically
+ have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
+ were for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
Q: How can I load a different font than the default? (default is an embedded version of ProggyClean.ttf, rendered at size 13)
A: Use the font atlas to load the TTF/OTF file you want:
@@ -469,8 +494,8 @@
io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
Q: How can I easily use icons in my application?
- A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you main font. Then you can refer to icons within your strings.
- Read 'How can I load multiple fonts?' and the file 'extra_fonts/README.txt' for instructions.
+ A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you main font. Then you can refer to icons within your
+ strings. Read 'How can I load multiple fonts?' and the file 'extra_fonts/README.txt' for instructions and useful header files.
Q: How can I load multiple fonts?
A: Use the font atlas to pack them into a single texture:
@@ -520,19 +545,24 @@
Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
Text input: it is up to your application to pass the right character code to io.AddInputCharacter(). The applications in examples/ are doing that.
- For languages using IME, on Windows you can copy the Hwnd of your application to io.ImeWindowHandle. The default implementation of io.ImeSetInputScreenPosFn() on Windows will set your IME position correctly.
+ For languages using IME, on Windows you can copy the Hwnd of your application to io.ImeWindowHandle.
+ The default implementation of io.ImeSetInputScreenPosFn() on Windows will set your IME position correctly.
- Q: How can I preserve my ImGui context across reloading a DLL? (loss of the global/static variables)
- A: Create your own context 'ctx = CreateContext()' + 'SetCurrentContext(ctx)' and your own font atlas 'ctx->GetIO().Fonts = new ImFontAtlas()' so you don't rely on the default globals.
+ Q: How can I preserve my Dear ImGui context across reloading a DLL? (loss of the global/static variables)
+ A: Create your own context 'ctx = CreateContext()' + 'SetCurrentContext(ctx)' and your own font atlas 'ctx->GetIO().Fonts = new ImFontAtlas()'
+ so you don't rely on the default globals.
Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
- A: The easiest way is to create a dummy window. Call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flag, zero background alpha,
- then retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
- You can also perfectly create a standalone ImDrawList instance _but_ you need ImGui to be initialized because ImDrawList pulls from ImGui data to retrieve the coordinates of the white pixel.
-
- - tip: the construct 'IMGUI_ONCE_UPON_A_FRAME { ... }' will run the block of code only once a frame. You can use it to quickly add custom UI in the middle of a deep nested inner loop in your code.
- - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug"
- - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window. this is also useful to set yourself in the context of another window (to get/set other settings)
+ A: The easiest way is to create a dummy window. Call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flag,
+ zero background alpha, then retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
+ You can also perfectly create a standalone ImDrawList instance _but_ you need ImGui to be initialized because ImDrawList pulls from ImGui
+ data to retrieve the coordinates of the white pixel.
+
+ - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window.
+ this is also useful to set yourself in the context of another window (to get/set other settings)
+ - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug".
+ - 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
+ of a deep nested inner loop in your code.
- tip: you can call Render() multiple times (e.g for VR renders).
- tip: call and read the ShowTestWindow() code in imgui_demo.cpp for more example of how to use ImGui!
@@ -582,16 +612,12 @@
#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'xxxx' to type 'xxxx' casts away qualifiers
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
-#pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
#endif
//-------------------------------------------------------------------------
// Forward Declarations
//-------------------------------------------------------------------------
-static void LogRenderedText(const ImVec2& ref_pos, const char* text, const char* text_end = NULL);
-
-static void PushMultiItemsWidths(int components, float w_full = 0.0f);
static float GetDraggedColumnOffset(int column_index);
static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true);
@@ -603,12 +629,11 @@ static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y
static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond);
static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond);
static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond);
-static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs);
+static ImGuiWindow* FindHoveredWindow(ImVec2 pos);
static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
-static inline bool IsWindowContentHoverable(ImGuiWindow* window);
static void ClearSetNextWindowData();
static void CheckStacksSize(ImGuiWindow* window, bool write);
-static void Scrollbar(ImGuiWindow* window, bool horizontal);
+static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
static void AddDrawListToRenderList(ImVector<ImDrawList*>& out_render_list, ImDrawList* draw_list);
static void AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiWindow* window);
@@ -622,10 +647,8 @@ static void MarkIniSettingsDirty(ImGuiWindow* window);
static ImRect GetVisibleRect();
-static bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags);
-static void CloseInactivePopups();
+static void CloseInactivePopups(ImGuiWindow* ref_window);
static void ClosePopupToLevel(int remaining);
-static void ClosePopup(ImGuiID id);
static ImGuiWindow* GetFrontMostModalRootWindow();
static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& rect_to_avoid);
@@ -638,6 +661,11 @@ static inline void DataTypeFormatString(ImGuiDataType data_type, void* data
static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2);
static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format);
+namespace ImGui
+{
+static void FocusPreviousWindow();
+}
+
//-----------------------------------------------------------------------------
// Platform dependent default implementations
//-----------------------------------------------------------------------------
@@ -695,49 +723,80 @@ ImGuiStyle::ImGuiStyle()
AntiAliasedShapes = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
CurveTessellationTol = 1.25f; // Tessellation tolerance. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
- Colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
- Colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
- Colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.70f);
- Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
- Colors[ImGuiCol_PopupBg] = ImVec4(0.05f, 0.05f, 0.10f, 0.90f);
- Colors[ImGuiCol_Border] = ImVec4(0.70f, 0.70f, 0.70f, 0.40f);
- Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
- Colors[ImGuiCol_FrameBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.30f); // Background of checkbox, radio button, plot, slider, text input
- Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.90f, 0.80f, 0.80f, 0.40f);
- Colors[ImGuiCol_FrameBgActive] = ImVec4(0.90f, 0.65f, 0.65f, 0.45f);
- Colors[ImGuiCol_TitleBg] = ImVec4(0.27f, 0.27f, 0.54f, 0.83f);
- Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.40f, 0.40f, 0.80f, 0.20f);
- Colors[ImGuiCol_TitleBgActive] = ImVec4(0.32f, 0.32f, 0.63f, 0.87f);
- Colors[ImGuiCol_MenuBarBg] = ImVec4(0.40f, 0.40f, 0.55f, 0.80f);
- Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.20f, 0.25f, 0.30f, 0.60f);
- Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.40f, 0.40f, 0.80f, 0.30f);
- Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f);
- Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 0.40f);
- Colors[ImGuiCol_ComboBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.99f);
- Colors[ImGuiCol_CheckMark] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f);
- Colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
- Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
- Colors[ImGuiCol_Button] = ImVec4(0.67f, 0.40f, 0.40f, 0.60f);
- Colors[ImGuiCol_ButtonHovered] = ImVec4(0.67f, 0.40f, 0.40f, 1.00f);
- Colors[ImGuiCol_ButtonActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
- Colors[ImGuiCol_Header] = ImVec4(0.40f, 0.40f, 0.90f, 0.45f);
- Colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.90f, 0.80f);
- Colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.87f, 0.80f);
- Colors[ImGuiCol_Separator] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
- Colors[ImGuiCol_SeparatorHovered] = ImVec4(0.60f, 0.60f, 0.70f, 1.00f);
- Colors[ImGuiCol_SeparatorActive] = ImVec4(0.70f, 0.70f, 0.90f, 1.00f);
- Colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
- Colors[ImGuiCol_ResizeGripHovered] = ImVec4(1.00f, 1.00f, 1.00f, 0.60f);
- Colors[ImGuiCol_ResizeGripActive] = ImVec4(1.00f, 1.00f, 1.00f, 0.90f);
- Colors[ImGuiCol_CloseButton] = ImVec4(0.50f, 0.50f, 0.90f, 0.50f);
- Colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.70f, 0.70f, 0.90f, 0.60f);
- Colors[ImGuiCol_CloseButtonActive] = ImVec4(0.70f, 0.70f, 0.70f, 1.00f);
- Colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
- Colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
- Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
- Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
- Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
- Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
+ ImGui::StyleColorsClassic(this);
+}
+
+void ImGui::StyleColorsClassic(ImGuiStyle* dst)
+{
+ ImGuiStyle* style = dst ? dst : &ImGui::GetStyle();
+ ImVec4* colors = style->Colors;
+
+ colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
+ colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
+ colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.70f);
+ colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
+ colors[ImGuiCol_PopupBg] = ImVec4(0.05f, 0.05f, 0.10f, 0.90f);
+ colors[ImGuiCol_Border] = ImVec4(0.70f, 0.70f, 0.70f, 0.40f);
+ colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
+ colors[ImGuiCol_FrameBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.30f); // Background of checkbox, radio button, plot, slider, text input
+ colors[ImGuiCol_FrameBgHovered] = ImVec4(0.90f, 0.80f, 0.80f, 0.40f);
+ colors[ImGuiCol_FrameBgActive] = ImVec4(0.90f, 0.65f, 0.65f, 0.45f);
+ colors[ImGuiCol_TitleBg] = ImVec4(0.27f, 0.27f, 0.54f, 0.83f);
+ colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.40f, 0.40f, 0.80f, 0.20f);
+ colors[ImGuiCol_TitleBgActive] = ImVec4(0.32f, 0.32f, 0.63f, 0.87f);
+ colors[ImGuiCol_MenuBarBg] = ImVec4(0.40f, 0.40f, 0.55f, 0.80f);
+ colors[ImGuiCol_ScrollbarBg] = ImVec4(0.20f, 0.25f, 0.30f, 0.60f);
+ colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.40f, 0.40f, 0.80f, 0.30f);
+ colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f);
+ colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 0.40f);
+ colors[ImGuiCol_ComboBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.99f);
+ colors[ImGuiCol_CheckMark] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f);
+ colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
+ colors[ImGuiCol_SliderGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
+ colors[ImGuiCol_Button] = ImVec4(0.67f, 0.40f, 0.40f, 0.60f);
+ colors[ImGuiCol_ButtonHovered] = ImVec4(0.67f, 0.40f, 0.40f, 1.00f);
+ colors[ImGuiCol_ButtonActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
+ colors[ImGuiCol_Header] = ImVec4(0.40f, 0.40f, 0.90f, 0.45f);
+ colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.90f, 0.80f);
+ colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.87f, 0.80f);
+ colors[ImGuiCol_Separator] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
+ colors[ImGuiCol_SeparatorHovered] = ImVec4(0.60f, 0.60f, 0.70f, 1.00f);
+ colors[ImGuiCol_SeparatorActive] = ImVec4(0.70f, 0.70f, 0.90f, 1.00f);
+ colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
+ colors[ImGuiCol_ResizeGripHovered] = ImVec4(1.00f, 1.00f, 1.00f, 0.60f);
+ colors[ImGuiCol_ResizeGripActive] = ImVec4(1.00f, 1.00f, 1.00f, 0.90f);
+ colors[ImGuiCol_CloseButton] = ImVec4(0.50f, 0.50f, 0.90f, 0.50f);
+ colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.70f, 0.70f, 0.90f, 0.60f);
+ colors[ImGuiCol_CloseButtonActive] = ImVec4(0.70f, 0.70f, 0.70f, 1.00f);
+ colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
+ colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
+ colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
+ colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
+ colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
+ colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
+}
+
+// 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.
+// Tips: if you need to change your scale multiple times, prefer calling this on a freshly initialized ImGuiStyle structure rather than scaling multiple times (because floating point multiplications are lossy).
+void ImGuiStyle::ScaleAllSizes(float scale_factor)
+{
+ WindowPadding *= scale_factor;
+ WindowMinSize *= scale_factor;
+ WindowRounding *= scale_factor;
+ ChildWindowRounding *= scale_factor;
+ FramePadding *= scale_factor;
+ FrameRounding *= scale_factor;
+ ItemSpacing *= scale_factor;
+ ItemInnerSpacing *= scale_factor;
+ TouchExtraPadding *= scale_factor;
+ IndentSpacing *= scale_factor;
+ ColumnsMinSpacing *= scale_factor;
+ ScrollbarSize *= scale_factor;
+ ScrollbarRounding *= scale_factor;
+ GrabMinSize *= scale_factor;
+ GrabRounding *= scale_factor;
+ DisplayWindowPadding *= scale_factor;
+ DisplaySafeAreaPadding *= scale_factor;
}
ImGuiIO::ImGuiIO()
@@ -751,25 +810,21 @@ ImGuiIO::ImGuiIO()
IniSavingRate = 5.0f;
IniFilename = "imgui.ini";
LogFilename = "imgui_log.txt";
- Fonts = &GImDefaultFontAtlas;
- FontGlobalScale = 1.0f;
- FontDefault = NULL;
- DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
- MousePos = ImVec2(-1,-1);
- MousePosPrev = ImVec2(-1,-1);
MouseDoubleClickTime = 0.30f;
MouseDoubleClickMaxDist = 6.0f;
- MouseDragThreshold = 6.0f;
- for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++)
- MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
- for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++)
- KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
for (int i = 0; i < ImGuiKey_COUNT; i++)
KeyMap[i] = -1;
KeyRepeatDelay = 0.250f;
KeyRepeatRate = 0.050f;
UserData = NULL;
+ Fonts = &GImDefaultFontAtlas;
+ FontGlobalScale = 1.0f;
+ FontDefault = NULL;
+ FontAllowUserScaling = false;
+ DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
+ DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f);
+
// User functions
RenderDrawListsFn = NULL;
MemAllocFn = malloc;
@@ -780,6 +835,13 @@ ImGuiIO::ImGuiIO()
ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
ImeWindowHandle = NULL;
+ // Input (NB: we already have memset zero the entire structure)
+ MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
+ MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
+ MouseDragThreshold = 6.0f;
+ for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
+ for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
+
// Set OS X style defaults based on __APPLE__ compile time flag
#ifdef __APPLE__
OSXBehaviors = true;
@@ -936,15 +998,30 @@ const char* ImStristr(const char* haystack, const char* haystack_end, const char
return NULL;
}
-// MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
+static const char* ImAtoi(const char* src, int* output)
+{
+ int negative = 0;
+ if (*src == '-') { negative = 1; src++; }
+ if (*src == '+') { src++; }
+ int v = 0;
+ while (*src >= '0' && *src <= '9')
+ v = (v * 10) + (*src++ - '0');
+ *output = negative ? -v : v;
+ return src;
+}
+
+// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
// 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.
+// B) When buf==NULL vsnprintf() will return the output size.
+#ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
int ImFormatString(char* buf, int buf_size, const char* fmt, ...)
{
- IM_ASSERT(buf_size > 0);
va_list args;
va_start(args, fmt);
int w = vsnprintf(buf, buf_size, fmt, args);
va_end(args);
+ if (buf == NULL)
+ return w;
if (w == -1 || w >= buf_size)
w = buf_size - 1;
buf[w] = 0;
@@ -953,13 +1030,15 @@ int ImFormatString(char* buf, int buf_size, const char* fmt, ...)
int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args)
{
- IM_ASSERT(buf_size > 0);
int w = vsnprintf(buf, buf_size, fmt, args);
+ if (buf == NULL)
+ return w;
if (w == -1 || w >= buf_size)
w = buf_size - 1;
buf[w] = 0;
return w;
}
+#endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
// Pass data_size==0 for zero-terminated strings
// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
@@ -1240,12 +1319,12 @@ void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float&
float K = 0.f;
if (g < b)
{
- const float tmp = g; g = b; b = tmp;
+ ImSwap(g, b);
K = -1.f;
}
if (r < g)
{
- const float tmp = r; r = g; g = tmp;
+ ImSwap(r, g);
K = -2.f / 6.f - K;
}
@@ -1595,7 +1674,7 @@ void ImGuiTextBuffer::appendv(const char* fmt, va_list args)
va_list args_copy;
va_copy(args_copy, args);
- int len = vsnprintf(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
+ int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
if (len <= 0)
return;
@@ -1608,7 +1687,7 @@ void ImGuiTextBuffer::appendv(const char* fmt, va_list args)
}
Buf.resize(needed_sz);
- ImFormatStringV(&Buf[write_off] - 1, len+1, fmt, args_copy);
+ ImFormatStringV(&Buf[write_off - 1], len + 1, fmt, args_copy);
}
void ImGuiTextBuffer::append(const char* fmt, ...)
@@ -1756,14 +1835,13 @@ ImGuiWindow::ImGuiWindow(const char* name)
Name = ImStrdup(name);
ID = ImHash(name, 0);
IDStack.push_back(ID);
- MoveId = GetID("#MOVE");
-
Flags = 0;
OrderWithinParent = 0;
PosFloat = Pos = ImVec2(0.0f, 0.0f);
Size = SizeFull = ImVec2(0.0f, 0.0f);
SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
WindowPadding = ImVec2(0.0f, 0.0f);
+ MoveId = GetID("#MOVE");
Scroll = ImVec2(0.0f, 0.0f);
ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
@@ -1774,6 +1852,7 @@ ImGuiWindow::ImGuiWindow(const char* name)
Accessed = false;
Collapsed = false;
SkipItems = false;
+ Appearing = false;
BeginCount = 0;
PopupId = 0;
AutoFitFramesX = AutoFitFramesY = -1;
@@ -1782,7 +1861,7 @@ ImGuiWindow::ImGuiWindow(const char* name)
AutoPosLastDirection = -1;
HiddenFrames = 0;
SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
- SetWindowPosCenterWanted = false;
+ SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
LastFrameActive = -1;
ItemWidthDefault = 0.0f;
@@ -1791,9 +1870,9 @@ ImGuiWindow::ImGuiWindow(const char* name)
DrawList = (ImDrawList*)ImGui::MemAlloc(sizeof(ImDrawList));
IM_PLACEMENT_NEW(DrawList) ImDrawList();
DrawList->_OwnerName = Name;
+ ParentWindow = NULL;
RootWindow = NULL;
RootNonPopupWindow = NULL;
- ParentWindow = NULL;
FocusIdxAllCounter = FocusIdxTabCounter = -1;
FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
@@ -1853,11 +1932,10 @@ ImGuiWindow* ImGui::GetParentWindow()
void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
+ g.ActiveIdIsJustActivated = (g.ActiveId != id);
g.ActiveId = id;
g.ActiveIdAllowOverlap = false;
- g.ActiveIdIsJustActivated = true;
- if (id)
- g.ActiveIdIsAlive = true;
+ g.ActiveIdIsAlive |= (id != 0);
g.ActiveIdWindow = window;
}
@@ -1880,27 +1958,51 @@ void ImGui::KeepAliveID(ImGuiID id)
g.ActiveIdIsAlive = true;
}
+static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
+{
+ // An active popup disable hovering on other windows (apart from its own children)
+ // FIXME-OPT: This could be cached/stored within the window.
+ ImGuiContext& g = *GImGui;
+ if (g.NavWindow)
+ if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
+ if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
+ {
+ // For the purpose of those flags we differentiate "standard popup" from "modal popup"
+ // NB: The order of those two tests is important because Modal windows are also Popups.
+ if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
+ return false;
+ if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
+ return false;
+ }
+
+ return true;
+}
+
// Advance cursor given item size for layout.
void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
{
- ImGuiWindow* window = GetCurrentWindow();
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
if (window->SkipItems)
return;
// Always align ourselves on pixel boundaries
- ImGuiContext& g = *GImGui;
const float line_height = ImMax(window->DC.CurrentLineHeight, size.y);
const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
+ //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]
window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
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));
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
-
- //window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // Debug
+ //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
window->DC.PrevLineHeight = line_height;
window->DC.PrevLineTextBaseOffset = text_base_offset;
window->DC.CurrentLineHeight = window->DC.CurrentLineTextBaseOffset = 0.0f;
+
+ // Horizontal layout mode
+ if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
+ SameLine();
}
void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
@@ -1911,73 +2013,96 @@ void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
// Declare item bounding box for clipping and interaction.
// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
// declares their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
-bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id)
+bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id)
{
- ImGuiWindow* window = GetCurrentWindow();
- window->DC.LastItemId = id ? *id : 0;
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ const bool is_clipped = IsClippedEx(bb, id, false);
+ window->DC.LastItemId = id;
window->DC.LastItemRect = bb;
- window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false;
- if (IsClippedEx(bb, id, false))
+ window->DC.LastItemRectHoveredRect = false;
+ if (is_clipped)
return false;
+ //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
- // This is a sensible default, but widgets are free to override it after calling ItemAdd()
+ // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
+ window->DC.LastItemRectHoveredRect = IsMouseHoveringRect(bb.Min, bb.Max);
+ return true;
+}
+
+// This is roughly matching the behavior of internal-facing ItemHoverable()
+// - 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())
+// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
+bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
+{
ImGuiContext& g = *GImGui;
- if (IsMouseHoveringRect(bb.Min, bb.Max))
- {
- // Matching the behavior of IsHovered() but allow if ActiveId==window->MoveID (we clicked on the window background)
- // So that clicking on items with no active id such as Text() still returns true with IsItemHovered()
- window->DC.LastItemHoveredRect = true;
- if (g.HoveredRootWindow == window->RootWindow)
- if (g.ActiveId == 0 || (id && g.ActiveId == *id) || g.ActiveIdAllowOverlap || (g.ActiveId == window->MoveId))
- if (IsWindowContentHoverable(window))
- window->DC.LastItemHoveredAndUsable = true;
- }
+ ImGuiWindow* window = g.CurrentWindow;
+ if (!window->DC.LastItemRectHoveredRect)
+ return false;
+ // [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.
+ // 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.
+ //if (g.HoveredWindow != window)
+ // return false;
+ if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
+ return false;
+ if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
+ if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
+ return false;
+ if (!IsWindowContentHoverable(window, flags))
+ return false;
+ if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
+ return false;
return true;
}
-bool ImGui::IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged)
+// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
+bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
{
ImGuiContext& g = *GImGui;
- ImGuiWindow* window = GetCurrentWindowRead();
- if (!bb.Overlaps(window->ClipRect))
- if (!id || *id != GImGui->ActiveId)
- if (clip_even_when_logged || !g.LogEnabled)
- return true;
- return false;
+ if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
+ return false;
+
+ ImGuiWindow* window = g.CurrentWindow;
+ if (g.HoveredWindow != window)
+ return false;
+ if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
+ return false;
+ if (!IsMouseHoveringRect(bb.Min, bb.Max))
+ return false;
+ if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_Default))
+ return false;
+ if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
+ return false;
+
+ SetHoveredID(id);
+ return true;
}
-// NB: This is an internal helper. The user-facing IsItemHovered() is using data emitted from ItemAdd(), with a slightly different logic.
-bool ImGui::IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs)
+bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
{
ImGuiContext& g = *GImGui;
- if (g.HoveredId == 0 || g.HoveredId == id || g.HoveredIdAllowOverlap)
- {
- ImGuiWindow* window = GetCurrentWindowRead();
- if (g.HoveredWindow == window || (flatten_childs && g.HoveredRootWindow == window->RootWindow))
- if ((g.ActiveId == 0 || g.ActiveId == id || g.ActiveIdAllowOverlap) && IsMouseHoveringRect(bb.Min, bb.Max))
- if (IsWindowContentHoverable(g.HoveredRootWindow))
- return true;
- }
+ ImGuiWindow* window = g.CurrentWindow;
+ if (!bb.Overlaps(window->ClipRect))
+ if (id == 0 || id != g.ActiveId)
+ if (clip_even_when_logged || !g.LogEnabled)
+ return true;
return false;
}
-bool ImGui::FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop)
+bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop)
{
ImGuiContext& g = *GImGui;
- const bool allow_keyboard_focus = window->DC.AllowKeyboardFocus;
+ const bool allow_keyboard_focus = (window->DC.ItemFlags & (ImGuiItemFlags_AllowKeyboardFocus | ImGuiItemFlags_Disabled)) == ImGuiItemFlags_AllowKeyboardFocus;
window->FocusIdxAllCounter++;
if (allow_keyboard_focus)
window->FocusIdxTabCounter++;
- // Process keyboard input at this point: TAB, Shift-TAB switch focus
- // We can always TAB out of a widget that doesn't allow tabbing in.
- if (tab_stop && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && is_active && IsKeyPressedMap(ImGuiKey_Tab))
- {
- // Modulo on index will be applied at the end of frame once we've got the total counter of items.
- window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1);
- }
+ // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item.
+ // Note that we can always TAB out of a widget that doesn't allow tabbing in.
+ if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab))
+ 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.
if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
return true;
@@ -2125,16 +2250,9 @@ void ImGui::NewFrame()
IM_ASSERT(g.Style.CurveTessellationTol > 0.0f); // Invalid style setting
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)
+ // Initialize on first frame
if (!g.Initialized)
- {
- // Initialize on first frame
- g.LogClipboard = (ImGuiTextBuffer*)ImGui::MemAlloc(sizeof(ImGuiTextBuffer));
- IM_PLACEMENT_NEW(g.LogClipboard) ImGuiTextBuffer();
-
- IM_ASSERT(g.Settings.empty());
- LoadIniSettingsFromDisk(g.IO.IniFilename);
- g.Initialized = true;
- }
+ ImGui::Initialize();
SetCurrentFont(GetDefaultFont());
IM_ASSERT(g.Font->IsLoaded());
@@ -2151,14 +2269,29 @@ void ImGui::NewFrame()
g.RenderDrawData.CmdLists = NULL;
g.RenderDrawData.CmdListsCount = g.RenderDrawData.TotalVtxCount = g.RenderDrawData.TotalIdxCount = 0;
+ // Clear reference to active widget if the widget isn't alive anymore
+ g.HoveredIdPreviousFrame = g.HoveredId;
+ g.HoveredId = 0;
+ g.HoveredIdAllowOverlap = false;
+ if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
+ ClearActiveID();
+ g.ActiveIdPreviousFrame = g.ActiveId;
+ g.ActiveIdIsAlive = false;
+ g.ActiveIdIsJustActivated = false;
+ if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
+ g.ScalarAsInputTextId = 0;
+
+ // Update keyboard input state
+ memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
+ for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
+ 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;
// Update mouse input state
- if (g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0)
- g.IO.MousePos = ImVec2(-9999.0f, -9999.0f);
- if ((g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0) || (g.IO.MousePosPrev.x < 0 && g.IO.MousePosPrev.y < 0)) // if mouse just appeared or disappeared (negative coordinate) we cancel out movement in MouseDelta
- g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
- else
+ // 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
+ if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
+ else
+ g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
g.IO.MousePosPrev = g.IO.MousePos;
for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
{
@@ -2187,9 +2320,6 @@ void ImGui::NewFrame()
g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]));
}
}
- memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
- for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
- 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;
// Calculate frame-rate for the user, as a purely luxurious feature
g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
@@ -2197,43 +2327,30 @@ void ImGui::NewFrame()
g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame));
- // Clear reference to active widget if the widget isn't alive anymore
- g.HoveredIdPreviousFrame = g.HoveredId;
- g.HoveredId = 0;
- g.HoveredIdAllowOverlap = false;
- if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
- ClearActiveID();
- g.ActiveIdPreviousFrame = g.ActiveId;
- g.ActiveIdIsAlive = false;
- g.ActiveIdIsJustActivated = false;
-
- // Handle user moving window (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows.
- if (g.MovedWindowMoveId && g.MovedWindowMoveId == g.ActiveId)
+ // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows.
+ if (g.MovingWindowMoveId && g.MovingWindowMoveId == g.ActiveId)
{
- KeepAliveID(g.MovedWindowMoveId);
- IM_ASSERT(g.MovedWindow && g.MovedWindow->RootWindow);
- IM_ASSERT(g.MovedWindow->RootWindow->MoveId == g.MovedWindowMoveId);
+ KeepAliveID(g.MovingWindowMoveId);
+ IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
+ IM_ASSERT(g.MovingWindow->MoveId == g.MovingWindowMoveId);
if (g.IO.MouseDown[0])
{
- if (!(g.MovedWindow->Flags & ImGuiWindowFlags_NoMove))
- {
- g.MovedWindow->PosFloat += g.IO.MouseDelta;
- if (!(g.MovedWindow->Flags & ImGuiWindowFlags_NoSavedSettings) && (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f))
- MarkIniSettingsDirty(g.MovedWindow);
- }
- FocusWindow(g.MovedWindow);
+ g.MovingWindow->RootWindow->PosFloat += g.IO.MouseDelta;
+ if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
+ MarkIniSettingsDirty(g.MovingWindow->RootWindow);
+ FocusWindow(g.MovingWindow);
}
else
{
ClearActiveID();
- g.MovedWindow = NULL;
- g.MovedWindowMoveId = 0;
+ g.MovingWindow = NULL;
+ g.MovingWindowMoveId = 0;
}
}
else
{
- g.MovedWindow = NULL;
- g.MovedWindowMoveId = 0;
+ g.MovingWindow = NULL;
+ g.MovingWindowMoveId = 0;
}
// Delay saving settings so we don't spam disk too much
@@ -2244,12 +2361,12 @@ void ImGui::NewFrame()
SaveIniSettingsToDisk(g.IO.IniFilename);
}
- // Find the window we are hovering. Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow
- g.HoveredWindow = g.MovedWindow ? g.MovedWindow : FindHoveredWindow(g.IO.MousePos, false);
- if (g.HoveredWindow && (g.HoveredWindow->Flags & ImGuiWindowFlags_ChildWindow))
- g.HoveredRootWindow = g.HoveredWindow->RootWindow;
- else
- g.HoveredRootWindow = g.MovedWindow ? g.MovedWindow->RootWindow : FindHoveredWindow(g.IO.MousePos, true);
+ // Find the window we are hovering
+ // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
+ // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point.
+ // - 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.
+ g.HoveredWindow = (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) ? g.MovingWindow : FindHoveredWindow(g.IO.MousePos);
+ g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
if (ImGuiWindow* modal_window = GetFrontMostModalRootWindow())
{
@@ -2265,7 +2382,7 @@ void ImGui::NewFrame()
g.ModalWindowDarkeningRatio = 0.0f;
}
- // Are we using inputs? Tell user so they can capture/discard the inputs away from the rest of their application.
+ // Update the WantCaptureMouse/WantCAptureKeyboard flags, so user can capture/discard the inputs away from the rest of their application.
// When clicking outside of a window we assume the click is owned by the application and won't request capture. We need to track click ownership.
int mouse_earliest_button_down = -1;
bool mouse_any_down = false;
@@ -2275,21 +2392,22 @@ void ImGui::NewFrame()
g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
mouse_any_down |= g.IO.MouseDown[i];
if (g.IO.MouseDown[i])
- if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[mouse_earliest_button_down] > g.IO.MouseClickedTime[i])
+ if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
mouse_earliest_button_down = i;
}
bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
- if (g.CaptureMouseNextFrame != -1)
- g.IO.WantCaptureMouse = (g.CaptureMouseNextFrame != 0);
+ if (g.WantCaptureMouseNextFrame != -1)
+ g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
else
- g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.ActiveId != 0) || (!g.OpenPopupStack.empty());
- g.IO.WantCaptureKeyboard = (g.CaptureKeyboardNextFrame != -1) ? (g.CaptureKeyboardNextFrame != 0) : (g.ActiveId != 0);
- g.IO.WantTextInput = (g.ActiveId != 0 && g.InputTextState.Id == g.ActiveId);
+ g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
+ g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != -1) ? (g.WantCaptureKeyboardNextFrame != 0) : (g.ActiveId != 0);
+ g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : 0;
g.MouseCursor = ImGuiMouseCursor_Arrow;
- g.CaptureMouseNextFrame = g.CaptureKeyboardNextFrame = -1;
+ g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
g.OsImePosRequest = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
// If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
+ // FIXME: For patterns of drag and drop between "application" and "imgui" we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
if (!mouse_avail_to_imgui)
g.HoveredWindow = g.HoveredRootWindow = NULL;
@@ -2319,7 +2437,6 @@ void ImGui::NewFrame()
}
// Pressing TAB activate widget focus
- // NB: Don't discard FocusedWindow if it isn't active, so that a window that go on/off programatically won't lose its keyboard focus.
if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && IsKeyPressedMap(ImGuiKey_Tab, false))
g.NavWindow->FocusIdxTabRequestNext = 0;
@@ -2334,25 +2451,32 @@ void ImGui::NewFrame()
// Closing the focused window restore focus to the first active root window in descending z-order
if (g.NavWindow && !g.NavWindow->WasActive)
- for (int i = g.Windows.Size-1; i >= 0; i--)
- if (g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow))
- {
- FocusWindow(g.Windows[i]);
- break;
- }
+ FocusPreviousWindow();
// No window should be open at the beginning of the frame.
// But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
g.CurrentWindowStack.resize(0);
g.CurrentPopupStack.resize(0);
- CloseInactivePopups();
+ CloseInactivePopups(g.NavWindow);
// Create implicit window - we will only render it if the user has added something to it.
+ // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
ImGui::SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
- ImGui::Begin("Debug");
+ ImGui::Begin("Debug##Default");
}
-// NB: behavior of ImGui after Shutdown() is not tested/guaranteed at the moment. This function is merely here to free heap allocations.
+void ImGui::Initialize()
+{
+ ImGuiContext& g = *GImGui;
+ g.LogClipboard = (ImGuiTextBuffer*)ImGui::MemAlloc(sizeof(ImGuiTextBuffer));
+ IM_PLACEMENT_NEW(g.LogClipboard) ImGuiTextBuffer();
+
+ IM_ASSERT(g.Settings.empty());
+ LoadIniSettingsFromDisk(g.IO.IniFilename);
+ g.Initialized = true;
+}
+
+// This function is merely here to free heap allocations.
void ImGui::Shutdown()
{
ImGuiContext& g = *GImGui;
@@ -2361,7 +2485,7 @@ void ImGui::Shutdown()
if (g.IO.Fonts) // Testing for NULL to allow user to NULLify in case of running Shutdown() on multiple contexts. Bit hacky.
g.IO.Fonts->Clear();
- // Cleanup of other data are conditional on actually having used ImGui.
+ // Cleanup of other data are conditional on actually having initialize ImGui.
if (!g.Initialized)
return;
@@ -2380,7 +2504,7 @@ void ImGui::Shutdown()
g.HoveredWindow = NULL;
g.HoveredRootWindow = NULL;
g.ActiveIdWindow = NULL;
- g.MovedWindow = NULL;
+ g.MovingWindow = NULL;
for (int i = 0; i < g.Settings.Size; i++)
ImGui::MemFree(g.Settings[i].Name);
g.Settings.clear();
@@ -2615,6 +2739,19 @@ static void AddWindowToRenderList(ImVector<ImDrawList*>& out_render_list, ImGuiW
}
}
+static void AddWindowToRenderListSelectLayer(ImGuiWindow* window)
+{
+ // FIXME: Generalize this with a proper layering system so e.g. user can draw in specific layers, below text, ..
+ ImGuiContext& g = *GImGui;
+ g.IO.MetricsActiveWindows++;
+ if (window->Flags & ImGuiWindowFlags_Popup)
+ AddWindowToRenderList(g.RenderDrawLists[1], window);
+ else if (window->Flags & ImGuiWindowFlags_Tooltip)
+ AddWindowToRenderList(g.RenderDrawLists[2], window);
+ else
+ AddWindowToRenderList(g.RenderDrawLists[0], window);
+}
+
// 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.
void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
{
@@ -2650,25 +2787,49 @@ void ImGui::EndFrame()
g.CurrentWindow->Active = false;
ImGui::End();
- // Click to focus window and start moving (after we're done with all our widgets)
- if (g.ActiveId == 0 && g.HoveredId == 0 && g.IO.MouseClicked[0])
+ if (g.ActiveId == 0 && g.HoveredId == 0)
{
- if (!(g.NavWindow && !g.NavWindow->WasActive && g.NavWindow->Active)) // Unless we just made a popup appear
+ if (!g.NavWindow || !g.NavWindow->Appearing) // Unless we just made a window/popup appear
{
- if (g.HoveredRootWindow != NULL)
+ // Click to focus window and start moving (after we're done with all our widgets)
+ if (g.IO.MouseClicked[0])
{
- FocusWindow(g.HoveredWindow);
- if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove))
+ if (g.HoveredRootWindow != NULL)
{
- g.MovedWindow = g.HoveredWindow;
- g.MovedWindowMoveId = g.HoveredRootWindow->MoveId;
- SetActiveID(g.MovedWindowMoveId, g.HoveredRootWindow);
+ FocusWindow(g.HoveredWindow);
+ if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove) && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoMove))
+ {
+ g.MovingWindow = g.HoveredWindow;
+ g.MovingWindowMoveId = g.MovingWindow->MoveId;
+ SetActiveID(g.MovingWindowMoveId, g.HoveredRootWindow);
+ }
+ }
+ else if (g.NavWindow != NULL && GetFrontMostModalRootWindow() == NULL)
+ {
+ // Clicking on void disable focus
+ FocusWindow(NULL);
}
}
- else if (g.NavWindow != NULL && GetFrontMostModalRootWindow() == NULL)
+
+ // With right mouse button we close popups without changing focus
+ // (The left mouse button path calls FocusWindow which will lead NewFrame->CloseInactivePopups to trigger)
+ if (g.IO.MouseClicked[1])
{
- // Clicking on void disable focus
- FocusWindow(NULL);
+ // Find the top-most window between HoveredWindow and the front most Modal Window.
+ // This is where we can trim the popup stack.
+ ImGuiWindow* modal = GetFrontMostModalRootWindow();
+ bool hovered_window_above_modal = false;
+ if (modal == NULL)
+ hovered_window_above_modal = true;
+ for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
+ {
+ ImGuiWindow* window = g.Windows[i];
+ if (window == modal)
+ break;
+ if (window == g.HoveredWindow)
+ hovered_window_above_modal = true;
+ }
+ CloseInactivePopups(hovered_window_above_modal ? g.HoveredWindow : modal);
}
}
}
@@ -2716,16 +2877,7 @@ void ImGui::Render()
{
ImGuiWindow* window = g.Windows[i];
if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0)
- {
- // FIXME: Generalize this with a proper layering system so e.g. user can draw in specific layers, below text, ..
- g.IO.MetricsActiveWindows++;
- if (window->Flags & ImGuiWindowFlags_Popup)
- AddWindowToRenderList(g.RenderDrawLists[1], window);
- else if (window->Flags & ImGuiWindowFlags_Tooltip)
- AddWindowToRenderList(g.RenderDrawLists[2], window);
- else
- AddWindowToRenderList(g.RenderDrawLists[0], window);
- }
+ AddWindowToRenderListSelectLayer(window);
}
// Flatten layers
@@ -2806,16 +2958,17 @@ void ImGui::LogText(const char* fmt, ...)
// Internal version that takes a position to decide on newline placement and pad items according to their depth.
// We split text into individual lines to add current tree level padding
-static void LogRenderedText(const ImVec2& ref_pos, const char* text, const char* text_end)
+static void LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL)
{
ImGuiContext& g = *GImGui;
- ImGuiWindow* window = ImGui::GetCurrentWindowRead();
+ ImGuiWindow* window = g.CurrentWindow;
if (!text_end)
text_end = ImGui::FindRenderedTextEnd(text, text_end);
- const bool log_new_line = ref_pos.y > window->DC.LogLinePosY+1;
- window->DC.LogLinePosY = ref_pos.y;
+ const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1);
+ if (ref_pos)
+ window->DC.LogLinePosY = ref_pos->y;
const char* text_remaining = text;
if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth
@@ -2860,7 +3013,7 @@ static void LogRenderedText(const ImVec2& ref_pos, const char* text, const char*
void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
{
ImGuiContext& g = *GImGui;
- ImGuiWindow* window = GetCurrentWindow();
+ ImGuiWindow* window = g.CurrentWindow;
// Hide anything after a '##' string
const char* text_display_end;
@@ -2880,14 +3033,14 @@ void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool
{
window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
if (g.LogEnabled)
- LogRenderedText(pos, text, text_display_end);
+ LogRenderedText(&pos, text, text_display_end);
}
}
void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
{
ImGuiContext& g = *GImGui;
- ImGuiWindow* window = GetCurrentWindow();
+ ImGuiWindow* window = g.CurrentWindow;
if (!text_end)
text_end = text + strlen(text); // FIXME-OPT
@@ -2897,7 +3050,7 @@ void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end
{
window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
if (g.LogEnabled)
- LogRenderedText(pos, text, text_end);
+ LogRenderedText(&pos, text, text_end);
}
}
@@ -2912,7 +3065,7 @@ void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, cons
return;
ImGuiContext& g = *GImGui;
- ImGuiWindow* window = GetCurrentWindow();
+ ImGuiWindow* window = g.CurrentWindow;
// Perform CPU side clipping for single clipped element to avoid using scissor state
ImVec2 pos = pos_min;
@@ -2939,14 +3092,14 @@ void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, cons
window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
}
if (g.LogEnabled)
- LogRenderedText(pos, text, text_display_end);
+ LogRenderedText(&pos, text, text_display_end);
}
// Render a rectangle shaped with optional rounding and borders
void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
{
- ImGuiWindow* window = GetCurrentWindow();
-
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
if (border && (window->Flags & ImGuiWindowFlags_ShowBorders))
{
@@ -2957,7 +3110,8 @@ void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border,
void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
{
- ImGuiWindow* window = GetCurrentWindow();
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
if (window->Flags & ImGuiWindowFlags_ShowBorders)
{
window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding);
@@ -2966,51 +3120,64 @@ void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
}
// Render a triangle to denote expanded/collapsed state
-void ImGui::RenderCollapseTriangle(ImVec2 p_min, bool is_open, float scale)
+void ImGui::RenderTriangle(ImVec2 p_min, ImGuiDir dir, float scale)
{
ImGuiContext& g = *GImGui;
- ImGuiWindow* window = GetCurrentWindow();
+ ImGuiWindow* window = g.CurrentWindow;
const float h = g.FontSize * 1.00f;
- const float r = h * 0.40f * scale;
- ImVec2 center = p_min + ImVec2(h*0.50f, h*0.50f*scale);
+ float r = h * 0.40f * scale;
+ ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale);
ImVec2 a, b, c;
- if (is_open)
- {
- center.y -= r*0.25f;
- a = center + ImVec2(0,1)*r;
- b = center + ImVec2(-0.866f,-0.5f)*r;
- c = center + ImVec2(0.866f,-0.5f)*r;
- }
- else
- {
- a = center + ImVec2(1,0)*r;
- b = center + ImVec2(-0.500f,0.866f)*r;
- c = center + ImVec2(-0.500f,-0.866f)*r;
+ switch (dir)
+ {
+ case ImGuiDir_Up:
+ r = -r; // ...fall through, no break!
+ case ImGuiDir_Down:
+ center.y -= r * 0.25f;
+ a = ImVec2(0,1) * r;
+ b = ImVec2(-0.866f,-0.5f) * r;
+ c = ImVec2(+0.866f,-0.5f) * r;
+ break;
+ case ImGuiDir_Left:
+ r = -r; // ...fall through, no break!
+ case ImGuiDir_Right:
+ a = ImVec2(1,0) * r;
+ b = ImVec2(-0.500f,+0.866f) * r;
+ c = ImVec2(-0.500f,-0.866f) * r;
+ break;
+ default:
+ IM_ASSERT(0);
+ break;
}
- window->DrawList->AddTriangleFilled(a, b, c, GetColorU32(ImGuiCol_Text));
+ window->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text));
}
void ImGui::RenderBullet(ImVec2 pos)
{
- ImGuiWindow* window = GetCurrentWindow();
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
window->DrawList->AddCircleFilled(pos, GImGui->FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8);
}
-void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col)
+void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
{
ImGuiContext& g = *GImGui;
- ImGuiWindow* window = GetCurrentWindow();
- float start_x = (float)(int)(g.FontSize * 0.307f + 0.5f);
- float rem_third = (float)(int)((g.FontSize - start_x) / 3.0f);
- float bx = pos.x + 0.5f + start_x + rem_third;
- float by = pos.y - 1.0f + (float)(int)(g.Font->Ascent * (g.FontSize / g.Font->FontSize) + 0.5f) + (float)(int)(g.Font->DisplayOffset.y);
- window->DrawList->PathLineTo(ImVec2(bx - rem_third, by - rem_third));
+ ImGuiWindow* window = g.CurrentWindow;
+
+ float thickness = ImMax(sz / 5.0f, 1.0f);
+ sz -= thickness*0.5f;
+ pos += ImVec2(thickness*0.25f, thickness*0.25f);
+
+ float third = sz / 3.0f;
+ float bx = pos.x + third;
+ float by = pos.y + sz - third*0.5f;
+ window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
window->DrawList->PathLineTo(ImVec2(bx, by));
- window->DrawList->PathLineTo(ImVec2(bx + rem_third*2, by - rem_third*2));
- window->DrawList->PathStroke(col, false);
+ window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
+ window->DrawList->PathStroke(col, false, thickness);
}
// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
@@ -3031,7 +3198,7 @@ ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_tex
return ImVec2(0.0f, font_size);
ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
- // Cancel out character spacing for the last character of a line (it is baked into glyph->XAdvance field)
+ // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field)
const float font_scale = font_size / font->FontSize;
const float character_spacing_x = 1.0f * font_scale;
if (text_size.x > 0.0f)
@@ -3047,7 +3214,7 @@ ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_tex
void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
{
ImGuiContext& g = *GImGui;
- ImGuiWindow* window = GetCurrentWindowRead();
+ ImGuiWindow* window = g.CurrentWindow;
if (g.LogEnabled)
{
// If logging is active, do not perform any clipping
@@ -3072,7 +3239,7 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items
// Find window given position, search front-to-back
// 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.
-static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs)
+static ImGuiWindow* FindHoveredWindow(ImVec2 pos)
{
ImGuiContext& g = *GImGui;
for (int i = g.Windows.Size-1; i >= 0; i--)
@@ -3082,10 +3249,8 @@ static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs)
continue;
if (window->Flags & ImGuiWindowFlags_NoInputs)
continue;
- if (excluding_childs && (window->Flags & ImGuiWindowFlags_ChildWindow) != 0)
- continue;
- // Using the clipped AABB so a child window will typically be clipped by its parent.
+ // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
ImRect bb(window->WindowRectClipped.Min - g.Style.TouchExtraPadding, window->WindowRectClipped.Max + g.Style.TouchExtraPadding);
if (bb.Contains(pos))
return window;
@@ -3099,7 +3264,7 @@ static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs)
bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
{
ImGuiContext& g = *GImGui;
- ImGuiWindow* window = GetCurrentWindowRead();
+ ImGuiWindow* window = g.CurrentWindow;
// Clip
ImRect rect_clipped(r_min, r_max);
@@ -3120,7 +3285,7 @@ bool ImGui::IsAnyWindowHovered()
static bool IsKeyPressedMap(ImGuiKey key, bool repeat)
{
const int key_index = GImGui->IO.KeyMap[key];
- return ImGui::IsKeyPressed(key_index, repeat);
+ return (key_index >= 0) ? ImGui::IsKeyPressed(key_index, repeat) : false;
}
int ImGui::GetKeyIndex(ImGuiKey imgui_key)
@@ -3137,6 +3302,25 @@ bool ImGui::IsKeyDown(int user_key_index)
return GImGui->IO.KeysDown[user_key_index];
}
+int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate)
+{
+ if (t == 0.0f)
+ return 1;
+ if (t <= repeat_delay || repeat_rate <= 0.0f)
+ return 0;
+ const int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t_prev - repeat_delay) / repeat_rate);
+ return (count > 0) ? count : 0;
+}
+
+int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
+{
+ ImGuiContext& g = *GImGui;
+ if (key_index < 0) return false;
+ IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
+ const float t = g.IO.KeysDownDuration[key_index];
+ return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate);
+}
+
bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
{
ImGuiContext& g = *GImGui;
@@ -3145,13 +3329,8 @@ bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
const float t = g.IO.KeysDownDuration[user_key_index];
if (t == 0.0f)
return true;
-
if (repeat && t > g.IO.KeyRepeatDelay)
- {
- float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
- if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
- return true;
- }
+ return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
return false;
}
@@ -3229,6 +3408,15 @@ ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
return g.IO.MousePos;
}
+// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position
+bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
+{
+ if (mouse_pos == NULL)
+ mouse_pos = &GImGui->IO.MousePos;
+ const float MOUSE_INVALID = -256000.0f;
+ return mouse_pos->x >= MOUSE_INVALID && mouse_pos->y >= MOUSE_INVALID;
+}
+
ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
{
ImGuiContext& g = *GImGui;
@@ -3261,24 +3449,12 @@ void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
void ImGui::CaptureKeyboardFromApp(bool capture)
{
- GImGui->CaptureKeyboardNextFrame = capture ? 1 : 0;
+ GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
}
void ImGui::CaptureMouseFromApp(bool capture)
{
- GImGui->CaptureMouseNextFrame = capture ? 1 : 0;
-}
-
-bool ImGui::IsItemHovered()
-{
- ImGuiWindow* window = GetCurrentWindowRead();
- return window->DC.LastItemHoveredAndUsable;
-}
-
-bool ImGui::IsItemRectHovered()
-{
- ImGuiWindow* window = GetCurrentWindowRead();
- return window->DC.LastItemHoveredRect;
+ GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
}
bool ImGui::IsItemActive()
@@ -3286,7 +3462,7 @@ bool ImGui::IsItemActive()
ImGuiContext& g = *GImGui;
if (g.ActiveId)
{
- ImGuiWindow* window = GetCurrentWindowRead();
+ ImGuiWindow* window = g.CurrentWindow;
return g.ActiveId == window->DC.LastItemId;
}
return false;
@@ -3294,7 +3470,7 @@ bool ImGui::IsItemActive()
bool ImGui::IsItemClicked(int mouse_button)
{
- return IsMouseClicked(mouse_button) && IsItemHovered();
+ return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_Default);
}
bool ImGui::IsAnyItemHovered()
@@ -3358,7 +3534,7 @@ static ImRect GetVisibleRect()
}
// Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first.
-static void BeginTooltipEx(bool override_previous_tooltip)
+void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
{
ImGuiContext& g = *GImGui;
char window_name[16];
@@ -3371,12 +3547,13 @@ static void BeginTooltipEx(bool override_previous_tooltip)
window->HiddenFrames = 1;
ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip%02d", ++g.TooltipOverrideCount);
}
- ImGui::Begin(window_name, NULL, ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize);
+ ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
+ ImGui::Begin(window_name, NULL, flags | extra_flags);
}
void ImGui::SetTooltipV(const char* fmt, va_list args)
{
- BeginTooltipEx(true);
+ BeginTooltipEx(0, true);
TextV(fmt, args);
EndTooltip();
}
@@ -3391,7 +3568,7 @@ void ImGui::SetTooltip(const char* fmt, ...)
void ImGui::BeginTooltip()
{
- BeginTooltipEx(false);
+ BeginTooltipEx(0, false);
}
void ImGui::EndTooltip()
@@ -3416,6 +3593,11 @@ void ImGui::OpenPopupEx(ImGuiID id, bool reopen_existing)
{
g.OpenPopupStack.resize(current_stack_size+1);
g.OpenPopupStack[current_stack_size] = popup_ref;
+
+ // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by CloseInactivePopups().
+ // This is equivalent to what ClosePopupToLevel() does.
+ if (g.OpenPopupStack[current_stack_size].PopupId == id)
+ FocusWindow(window);
}
}
@@ -3425,16 +3607,16 @@ void ImGui::OpenPopup(const char* str_id)
OpenPopupEx(g.CurrentWindow->GetID(str_id), false);
}
-static void CloseInactivePopups()
+static void CloseInactivePopups(ImGuiWindow* ref_window)
{
ImGuiContext& g = *GImGui;
if (g.OpenPopupStack.empty())
return;
// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
- // Don't close our own child popup windows
+ // Don't close our own child popup windows.
int n = 0;
- if (g.NavWindow)
+ if (ref_window)
{
for (n = 0; n < g.OpenPopupStack.Size; n++)
{
@@ -3445,24 +3627,25 @@ static void CloseInactivePopups()
if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
continue;
+ // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow)
bool has_focus = false;
for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++)
- has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == g.NavWindow->RootWindow);
+ has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow);
if (!has_focus)
break;
}
}
- if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a useful breakpoint on the line below
- g.OpenPopupStack.resize(n);
+ if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the block below
+ ClosePopupToLevel(n);
}
static ImGuiWindow* GetFrontMostModalRootWindow()
{
ImGuiContext& g = *GImGui;
for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
- if (ImGuiWindow* front_most_popup = g.OpenPopupStack.Data[n].Window)
- if (front_most_popup->Flags & ImGuiWindowFlags_Modal)
- return front_most_popup;
+ if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
+ if (popup->Flags & ImGuiWindowFlags_Modal)
+ return popup;
return NULL;
}
@@ -3476,9 +3659,9 @@ static void ClosePopupToLevel(int remaining)
g.OpenPopupStack.resize(remaining);
}
-static void ClosePopup(ImGuiID id)
+void ImGui::ClosePopup(ImGuiID id)
{
- if (!ImGui::IsPopupOpen(id))
+ if (!IsPopupOpen(id))
return;
ImGuiContext& g = *GImGui;
ClosePopupToLevel(g.OpenPopupStack.Size - 1);
@@ -3504,18 +3687,18 @@ static inline void ClearSetNextWindowData()
g.SetNextWindowSizeConstraint = g.SetNextWindowFocus = false;
}
-static bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
+bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
- if (!ImGui::IsPopupOpen(id))
+ if (!IsPopupOpen(id))
{
ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
return false;
}
- ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
- ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
+ PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
+ ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings;
char name[20];
if (flags & ImGuiWindowFlags_ChildMenu)
@@ -3523,11 +3706,11 @@ static bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
else
ImFormatString(name, IM_ARRAYSIZE(name), "##popup_%08x", id); // Not recycling, so we can close/open during the same frame
- bool is_open = ImGui::Begin(name, NULL, flags);
+ bool is_open = Begin(name, NULL, flags);
if (!(window->Flags & ImGuiWindowFlags_ShowBorders))
g.CurrentWindow->Flags &= ~ImGuiWindowFlags_ShowBorders;
if (!is_open) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
- ImGui::EndPopup();
+ EndPopup();
return is_open;
}
@@ -3540,7 +3723,7 @@ bool ImGui::BeginPopup(const char* str_id)
ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
return false;
}
- return BeginPopupEx(g.CurrentWindow->GetID(str_id), ImGuiWindowFlags_ShowBorders);
+ return BeginPopupEx(g.CurrentWindow->GetID(str_id), ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_AlwaysAutoResize);
}
bool ImGui::IsPopupOpen(ImGuiID id)
@@ -3566,11 +3749,15 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags ext
return false;
}
+ // Center modal windows by default
+ if ((window->SetWindowPosAllowFlags & g.SetNextWindowPosCond) == 0)
+ SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
+
ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoSavedSettings;
- bool is_open = ImGui::Begin(name, p_open, flags);
+ bool is_open = Begin(name, p_open, flags);
if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
{
- ImGui::EndPopup();
+ EndPopup();
if (is_open)
ClosePopup(id);
return false;
@@ -3584,49 +3771,64 @@ void ImGui::EndPopup()
ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
IM_ASSERT(GImGui->CurrentPopupStack.Size > 0);
- ImGui::End();
+ End();
if (!(window->Flags & ImGuiWindowFlags_Modal))
- ImGui::PopStyleVar();
+ PopStyleVar();
+}
+
+bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
+{
+ ImGuiWindow* window = GImGui->CurrentWindow;
+ if (IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
+ {
+ 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!
+ IM_ASSERT(id != 0); // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
+ OpenPopupEx(id, true);
+ return true;
+ }
+ return false;
}
-// This is a helper to handle the most simple case of associating one named popup to one given widget.
-// 1. If you have many possible popups (for different "instances" of a same widget, or for wholly different widgets), you may be better off handling
-// this yourself so you can store data relative to the widget that opened the popup instead of choosing different popup identifiers.
-// 2. If you want right-clicking on the same item to reopen the popup at new location, use the same code replacing IsItemHovered() with IsItemRectHovered()
-// and passing true to the OpenPopupEx().
-// Because: hovering an item in a window below the popup won't normally trigger is hovering behavior/coloring. The pattern of ignoring the fact that
-// the item can be interacted with (because it is blocked by the active popup) may useful in some situation when e.g. large canvas as one item, content of menu
-// driven by click position.
+// This is a helper to handle the simplest case of associating one named popup to one given widget.
+// You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
+// You can pass a NULL str_id to use the identifier of the last item.
bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
{
- if (IsItemHovered() && IsMouseClicked(mouse_button))
- OpenPopupEx(GImGui->CurrentWindow->GetID(str_id), false);
- return BeginPopup(str_id);
+ ImGuiWindow* window = GImGui->CurrentWindow;
+ 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!
+ IM_ASSERT(id != 0); // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
+ if (IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
+ OpenPopupEx(id, true);
+ return BeginPopupEx(id, ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_AlwaysAutoResize);
}
bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
{
if (!str_id)
str_id = "window_context";
- if (IsMouseHoveringWindow() && IsMouseClicked(mouse_button))
- if (also_over_items || !IsAnyItemHovered())
- OpenPopupEx(GImGui->CurrentWindow->GetID(str_id), true);
- return BeginPopup(str_id);
+ ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
+ if (IsMouseClicked(mouse_button))
+ if (IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
+ if (also_over_items || !IsAnyItemHovered())
+ OpenPopupEx(id, true);
+ return BeginPopupEx(id, ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_AlwaysAutoResize);
}
bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
{
if (!str_id)
str_id = "void_context";
- if (!IsMouseHoveringAnyWindow() && IsMouseClicked(mouse_button))
- OpenPopupEx(GImGui->CurrentWindow->GetID(str_id), true);
- return BeginPopup(str_id);
+ ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
+ if (!IsAnyWindowHovered() && IsMouseClicked(mouse_button))
+ OpenPopupEx(id, true);
+ return BeginPopupEx(id, ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_AlwaysAutoResize);
}
static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
{
ImGuiWindow* parent_window = ImGui::GetCurrentWindow();
ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
+ flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
const ImVec2 content_avail = ImGui::GetContentRegionAvail();
ImVec2 size = ImFloor(size_arg);
@@ -3641,11 +3843,12 @@ static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b
char title[256];
if (name)
- ImFormatString(title, IM_ARRAYSIZE(title), "%s.%s.%08X", parent_window->Name, name, id);
+ ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
else
- ImFormatString(title, IM_ARRAYSIZE(title), "%s.%08X", parent_window->Name, id);
+ ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
- bool ret = ImGui::Begin(title, NULL, size, -1.0f, flags);
+ ImGui::SetNextWindowSize(size);
+ bool ret = ImGui::Begin(title, NULL, flags);
ImGuiWindow* child_window = ImGui::GetCurrentWindow();
child_window->AutoFitChildAxises = auto_fit_axises;
if (!(parent_window->Flags & ImGuiWindowFlags_ShowBorders))
@@ -3682,13 +3885,12 @@ void ImGui::EndChild()
sz.x = ImMax(4.0f, sz.x);
if (window->AutoFitChildAxises & 0x02)
sz.y = ImMax(4.0f, sz.y);
-
ImGui::End();
ImGuiWindow* parent_window = GetCurrentWindow();
ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
ItemSize(sz);
- ItemAdd(bb, NULL);
+ ItemAdd(bb, 0);
}
}
@@ -3697,17 +3899,17 @@ bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags ext
{
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
- ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, style.Colors[ImGuiCol_FrameBg]);
- ImGui::PushStyleVar(ImGuiStyleVar_ChildWindowRounding, style.FrameRounding);
- ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
- return ImGui::BeginChild(id, size, (g.CurrentWindow->Flags & ImGuiWindowFlags_ShowBorders) ? true : false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
+ PushStyleColor(ImGuiCol_ChildWindowBg, style.Colors[ImGuiCol_FrameBg]);
+ PushStyleVar(ImGuiStyleVar_ChildWindowRounding, style.FrameRounding);
+ PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
+ return BeginChild(id, size, (g.CurrentWindow->Flags & ImGuiWindowFlags_ShowBorders) ? true : false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
}
void ImGui::EndChildFrame()
{
- ImGui::EndChild();
- ImGui::PopStyleVar(2);
- ImGui::PopStyleColor();
+ EndChild();
+ PopStyleVar(2);
+ PopStyleColor();
}
// Save and compare stack sizes on Begin()/End() to detect usage errors
@@ -3830,7 +4032,7 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl
return window;
}
-static void ApplySizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size)
+static ImVec2 CalcSizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size)
{
ImGuiContext& g = *GImGui;
if (g.SetNextWindowSizeConstraint)
@@ -3852,25 +4054,72 @@ static void ApplySizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size)
}
if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
new_size = ImMax(new_size, g.Style.WindowMinSize);
- window->SizeFull = new_size;
+ return new_size;
+}
+
+static ImVec2 CalcSizeAutoFit(ImGuiWindow* window)
+{
+ ImGuiContext& g = *GImGui;
+ ImGuiStyle& style = g.Style;
+ ImGuiWindowFlags flags = window->Flags;
+ ImVec2 size_auto_fit;
+ if ((flags & ImGuiWindowFlags_Tooltip) != 0)
+ {
+ // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose.
+ size_auto_fit = window->SizeContents + window->WindowPadding - ImVec2(0.0f, style.ItemSpacing.y);
+ }
+ else
+ {
+ // Handling case of auto fit window not fitting on the screen (on either axis): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
+ size_auto_fit = ImClamp(window->SizeContents + window->WindowPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding));
+ ImVec2 size_auto_fit_after_constraint = CalcSizeFullWithConstraint(window, size_auto_fit);
+ if (size_auto_fit_after_constraint.x < window->SizeContents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar))
+ size_auto_fit.y += style.ScrollbarSize;
+ if (size_auto_fit_after_constraint.y < window->SizeContents.y && !(flags & ImGuiWindowFlags_NoScrollbar))
+ size_auto_fit.x += style.ScrollbarSize * 2.0f;
+ size_auto_fit.y = ImMax(size_auto_fit.y - style.ItemSpacing.y, 0.0f);
+ }
+ return size_auto_fit;
+}
+
+static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
+{
+ ImVec2 scroll = window->Scroll;
+ float cr_x = window->ScrollTargetCenterRatio.x;
+ float cr_y = window->ScrollTargetCenterRatio.y;
+ if (window->ScrollTarget.x < FLT_MAX)
+ scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
+ if (window->ScrollTarget.y < FLT_MAX)
+ scroll.y = window->ScrollTarget.y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y);
+ scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
+ if (!window->Collapsed && !window->SkipItems)
+ {
+ scroll.x = ImMin(scroll.x, ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x))); // == GetScrollMaxX for that window
+ scroll.y = ImMin(scroll.y, ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y))); // == GetScrollMaxY for that window
+ }
+ return scroll;
+}
+
+static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
+{
+ if (flags & ImGuiWindowFlags_ComboBox)
+ return ImGuiCol_ComboBg;
+ if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
+ return ImGuiCol_PopupBg;
+ if (flags & ImGuiWindowFlags_ChildWindow)
+ return ImGuiCol_ChildWindowBg;
+ return ImGuiCol_WindowBg;
}
// Push a new ImGui window to add widgets to.
// - 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.
// - Begin/End can be called multiple times during the frame with the same window name to append content.
-// - 'size_on_first_use' for a regular window denote the initial size for first-time creation (no saved data) and isn't that useful. Use SetNextWindowSize() prior to calling Begin() for more flexible window manipulation.
// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
// 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.
// - 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.
// - 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.
-// - Passing non-zero 'size' is roughly equivalent to calling SetNextWindowSize(size, ImGuiCond_FirstUseEver) prior to calling Begin().
bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
{
- return ImGui::Begin(name, p_open, ImVec2(0.f, 0.f), -1.0f, flags);
-}
-
-bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha, ImGuiWindowFlags flags)
-{
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
IM_ASSERT(name != NULL); // Window name required
@@ -3885,11 +4134,12 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
ImGuiWindow* window = FindWindowByName(name);
if (!window)
{
+ ImVec2 size_on_first_use = (g.SetNextWindowSizeCond != 0) ? g.SetNextWindowSizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here.
window = CreateNewWindow(name, size_on_first_use, flags);
window_is_new = true;
}
- const int current_frame = ImGui::GetFrameCount();
+ const int current_frame = g.FrameCount;
const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
if (first_begin_of_the_frame)
window->Flags = (ImGuiWindowFlags)flags;
@@ -3903,41 +4153,45 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
CheckStacksSize(window, true);
IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
- bool window_was_active = (window->LastFrameActive == current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
+ bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
if (flags & ImGuiWindowFlags_Popup)
{
ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
- window_was_active &= (window->PopupId == popup_ref.PopupId);
- window_was_active &= (window == popup_ref.Window);
+ window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
+ window_just_activated_by_user |= (window != popup_ref.Window);
popup_ref.Window = window;
g.CurrentPopupStack.push_back(popup_ref);
window->PopupId = popup_ref.PopupId;
}
- const bool window_appearing_after_being_hidden = (window->HiddenFrames == 1);
+ const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames == 1);
+ window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
// Process SetNextWindow***() calls
bool window_pos_set_by_api = false, window_size_set_by_api = false;
if (g.SetNextWindowPosCond)
{
- const ImVec2 backup_cursor_pos = window->DC.CursorPos; // FIXME: not sure of the exact reason of this saving/restore anymore :( need to look into that.
- if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowPosAllowFlags |= ImGuiCond_Appearing;
+ if (window->Appearing)
+ window->SetWindowPosAllowFlags |= ImGuiCond_Appearing;
window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.SetNextWindowPosCond) != 0;
- if (window_pos_set_by_api && ImLengthSqr(g.SetNextWindowPosVal - ImVec2(-FLT_MAX,-FLT_MAX)) < 0.001f)
+ if (window_pos_set_by_api && ImLengthSqr(g.SetNextWindowPosPivot) > 0.00001f)
{
- window->SetWindowPosCenterWanted = true; // May be processed on the next frame if this is our first frame and we are measuring size
+ // May be processed on the next frame if this is our first frame and we are measuring size
+ // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
+ window->SetWindowPosVal = g.SetNextWindowPosVal;
+ window->SetWindowPosPivot = g.SetNextWindowPosPivot;
window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
}
else
{
SetWindowPos(window, g.SetNextWindowPosVal, g.SetNextWindowPosCond);
}
- window->DC.CursorPos = backup_cursor_pos;
g.SetNextWindowPosCond = 0;
}
if (g.SetNextWindowSizeCond)
{
- if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowSizeAllowFlags |= ImGuiCond_Appearing;
+ if (window->Appearing)
+ window->SetWindowSizeAllowFlags |= ImGuiCond_Appearing;
window_size_set_by_api = (window->SetWindowSizeAllowFlags & g.SetNextWindowSizeCond) != 0;
SetWindowSize(window, g.SetNextWindowSizeVal, g.SetNextWindowSizeCond);
g.SetNextWindowSizeCond = 0;
@@ -3953,13 +4207,14 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
}
if (g.SetNextWindowCollapsedCond)
{
- if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowCollapsedAllowFlags |= ImGuiCond_Appearing;
+ if (window->Appearing)
+ window->SetWindowCollapsedAllowFlags |= ImGuiCond_Appearing;
SetWindowCollapsed(window, g.SetNextWindowCollapsedVal, g.SetNextWindowCollapsedCond);
g.SetNextWindowCollapsedCond = 0;
}
if (g.SetNextWindowFocus)
{
- ImGui::SetWindowFocus();
+ SetWindowFocus();
g.SetNextWindowFocus = false;
}
@@ -3969,11 +4224,11 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
if (!(g.CurrentWindowStack[root_idx]->Flags & ImGuiWindowFlags_ChildWindow))
break;
for (root_non_popup_idx = root_idx; root_non_popup_idx > 0; root_non_popup_idx--)
- if (!(g.CurrentWindowStack[root_non_popup_idx]->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
+ if (!(g.CurrentWindowStack[root_non_popup_idx]->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) || (g.CurrentWindowStack[root_non_popup_idx]->Flags & ImGuiWindowFlags_Modal))
break;
window->ParentWindow = parent_window;
window->RootWindow = g.CurrentWindowStack[root_idx];
- window->RootNonPopupWindow = g.CurrentWindowStack[root_non_popup_idx]; // This is merely for displaying the TitleBgActive color.
+ window->RootNonPopupWindow = g.CurrentWindowStack[root_non_popup_idx]; // Used to display TitleBgActive color and for selecting which window to use for NavWindowing
// When reusing window again multiple times a frame, just append content (don't need to setup again)
if (first_begin_of_the_frame)
@@ -3994,7 +4249,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
else
PushClipRect(fullscreen_rect.Min, fullscreen_rect.Max, true);
- if (!window_was_active)
+ if (window_just_activated_by_user)
{
// Popup first latch mouse position, will position itself when it appears next frame
window->AutoPosLastDirection = -1;
@@ -4028,7 +4283,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
// Hide popup/tooltip window when first appearing while we measure size (because we recycle them)
if (window->HiddenFrames > 0)
window->HiddenFrames--;
- if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && !window_was_active)
+ if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && window_just_activated_by_user)
{
window->HiddenFrames = 1;
if (flags & ImGuiWindowFlags_AlwaysAutoResize)
@@ -4042,26 +4297,8 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
// Lock window padding so that altering the ShowBorders flag for children doesn't have side-effects.
window->WindowPadding = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_ComboBox | ImGuiWindowFlags_Popup))) ? ImVec2(0,0) : style.WindowPadding;
- // Calculate auto-fit size
- ImVec2 size_auto_fit;
- if ((flags & ImGuiWindowFlags_Tooltip) != 0)
- {
- // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose.
- size_auto_fit = window->SizeContents + window->WindowPadding - ImVec2(0.0f, style.ItemSpacing.y);
- }
- else
- {
- size_auto_fit = ImClamp(window->SizeContents + window->WindowPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding));
-
- // Handling case of auto fit window not fitting in screen on one axis, we are growing auto fit size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
- if (size_auto_fit.x < window->SizeContents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar))
- size_auto_fit.y += style.ScrollbarSize;
- if (size_auto_fit.y < window->SizeContents.y && !(flags & ImGuiWindowFlags_NoScrollbar))
- size_auto_fit.x += style.ScrollbarSize;
- size_auto_fit.y = ImMax(size_auto_fit.y - style.ItemSpacing.y, 0.0f);
- }
-
- // Handle automatic resize
+ // Calculate auto-fit size, handle automatic resize
+ const ImVec2 size_auto_fit = CalcSizeAutoFit(window);
if (window->Collapsed)
{
// We still process initial auto-fit on collapsed windows to get a window width,
@@ -4071,13 +4308,13 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
if (window->AutoFitFramesY > 0)
window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
}
- else
+ else if (!window_size_set_by_api)
{
- if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window_size_set_by_api)
+ if (flags & ImGuiWindowFlags_AlwaysAutoResize)
{
window->SizeFull = size_auto_fit;
}
- else if ((window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) && !window_size_set_by_api)
+ else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
{
// Auto-fit only grows during the first few frames
if (window->AutoFitFramesX > 0)
@@ -4089,7 +4326,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
}
// Apply minimum/maximum window size constraints and final size
- ApplySizeFullWithConstraint(window, window->SizeFull);
+ window->SizeFull = CalcSizeFullWithConstraint(window, window->SizeFull);
window->Size = window->Collapsed ? window->TitleBarRect().GetSize() : window->SizeFull;
// POSITION
@@ -4102,17 +4339,16 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
}
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))
{
+ IM_ASSERT(window_size_set_by_api); // Submitted by BeginChild()
window->Pos = window->PosFloat = parent_window->DC.CursorPos;
- window->Size = window->SizeFull = size_on_first_use; // NB: argument name 'size_on_first_use' misleading here, it's really just 'size' as provided by user passed via BeginChild()->Begin().
+ window->Size = window->SizeFull;
}
- bool window_pos_center = false;
- window_pos_center |= (window->SetWindowPosCenterWanted && window->HiddenFrames == 0);
- window_pos_center |= ((flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api && window_appearing_after_being_hidden);
- if (window_pos_center)
+ const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFrames == 0);
+ if (window_pos_with_pivot)
{
- // Center (any sort of window)
- SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, fullscreen_rect.GetCenter() - window->SizeFull * 0.5f), 0);
+ // Position given a pivot (e.g. for centering)
+ SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0);
}
else if (flags & ImGuiWindowFlags_ChildMenu)
{
@@ -4127,7 +4363,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
rect_to_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);
window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
}
- else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_appearing_after_being_hidden)
+ else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
{
ImRect rect_to_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1);
window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
@@ -4136,10 +4372,11 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
// Position tooltip (always follows mouse)
if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api)
{
- ImRect rect_to_avoid(g.IO.MousePos.x - 16, g.IO.MousePos.y - 8, g.IO.MousePos.x + 24, g.IO.MousePos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead?
- window->PosFloat = FindBestPopupWindowPos(g.IO.MousePos, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
+ ImVec2 ref_pos = g.IO.MousePos;
+ ImRect rect_to_avoid(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24, ref_pos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead?
+ window->PosFloat = FindBestPopupWindowPos(ref_pos, window->Size, &window->AutoPosLastDirection, rect_to_avoid);
if (window->AutoPosLastDirection == -1)
- window->PosFloat = g.IO.MousePos + 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.
+ window->PosFloat = 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.
}
// Clamp position so it stays visible
@@ -4167,23 +4404,8 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
// Apply scrolling
- if (window->ScrollTarget.x < FLT_MAX)
- {
- window->Scroll.x = window->ScrollTarget.x;
- window->ScrollTarget.x = FLT_MAX;
- }
- if (window->ScrollTarget.y < FLT_MAX)
- {
- float center_ratio = window->ScrollTargetCenterRatio.y;
- window->Scroll.y = window->ScrollTarget.y - ((1.0f - center_ratio) * (window->TitleBarHeight() + window->MenuBarHeight())) - (center_ratio * (window->SizeFull.y - window->ScrollbarSizes.y));
- window->ScrollTarget.y = FLT_MAX;
- }
- window->Scroll = ImMax(window->Scroll, ImVec2(0.0f, 0.0f));
- if (!window->Collapsed && !window->SkipItems)
- {
- window->Scroll.x = ImMin(window->Scroll.x, GetScrollMaxX());
- window->Scroll.y = ImMin(window->Scroll.y, GetScrollMaxY());
- }
+ window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
+ window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
// Modal window darkens what is behind them
if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow())
@@ -4194,8 +4416,8 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
const float window_rounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding;
if (window->Collapsed)
{
- // Draw title bar only
- RenderFrame(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32(ImGuiCol_TitleBgCollapsed), true, window_rounding);
+ // Title bar only
+ RenderFrame(title_bar_rect.Min, title_bar_rect.Max, GetColorU32(ImGuiCol_TitleBgCollapsed), true, window_rounding);
}
else
{
@@ -4204,30 +4426,34 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && !(flags & ImGuiWindowFlags_NoResize))
{
// Manual resize
+ // Using the FlattenChilds button flag, we make the resize button accessible even if we are hovering over a child window
const ImVec2 br = window->Rect().GetBR();
const ImRect resize_rect(br - ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f), br);
const ImGuiID resize_id = window->GetID("#RESIZE");
bool hovered, held;
ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds);
resize_col = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
-
if (hovered || held)
g.MouseCursor = ImGuiMouseCursor_ResizeNWSE;
+ ImVec2 size_target(FLT_MAX,FLT_MAX);
if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0])
{
// Manual auto-fit when double-clicking
- ApplySizeFullWithConstraint(window, size_auto_fit);
- MarkIniSettingsDirty(window);
+ size_target = size_auto_fit;
ClearActiveID();
}
else if (held)
{
// We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
- ApplySizeFullWithConstraint(window, (g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize()) - window->Pos);
- MarkIniSettingsDirty(window);
+ size_target = (g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize()) - window->Pos;
}
+ if (size_target.x != FLT_MAX && size_target.y != FLT_MAX)
+ {
+ window->SizeFull = CalcSizeFullWithConstraint(window, size_target);
+ MarkIniSettingsDirty(window);
+ }
window->Size = window->SizeFull;
title_bar_rect = window->TitleBarRect();
}
@@ -4241,23 +4467,13 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
window->BorderSize = (flags & ImGuiWindowFlags_ShowBorders) ? 1.0f : 0.0f;
// Window background, Default Alpha
- ImGuiCol bg_color_idx = ImGuiCol_WindowBg;
- if ((flags & ImGuiWindowFlags_ComboBox) != 0)
- bg_color_idx = ImGuiCol_ComboBg;
- else if ((flags & ImGuiWindowFlags_Tooltip) != 0 || (flags & ImGuiWindowFlags_Popup) != 0)
- bg_color_idx = ImGuiCol_PopupBg;
- else if ((flags & ImGuiWindowFlags_ChildWindow) != 0)
- bg_color_idx = ImGuiCol_ChildWindowBg;
- ImVec4 bg_color = style.Colors[bg_color_idx]; // We don't use GetColorU32() because bg_alpha is assigned (not multiplied) below
- if (bg_alpha >= 0.0f)
- bg_color.w = bg_alpha;
- bg_color.w *= style.Alpha;
- if (bg_color.w > 0.0f)
- window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, ColorConvertFloat4ToU32(bg_color), window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImGuiCorner_All : ImGuiCorner_BottomLeft|ImGuiCorner_BottomRight);
+ ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
+ window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImGuiCorner_All : ImGuiCorner_BotLeft|ImGuiCorner_BotRight);
// Title bar
+ const bool is_focused = g.NavWindow && window->RootNonPopupWindow == g.NavWindow->RootNonPopupWindow;
if (!(flags & ImGuiWindowFlags_NoTitleBar))
- window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32((g.NavWindow && window->RootNonPopupWindow == g.NavWindow->RootNonPopupWindow) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, ImGuiCorner_TopLeft|ImGuiCorner_TopRight);
+ window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32(is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, ImGuiCorner_TopLeft|ImGuiCorner_TopRight);
// Menu bar
if (flags & ImGuiWindowFlags_MenuBar)
@@ -4270,9 +4486,9 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
// Scrollbars
if (window->ScrollbarX)
- Scrollbar(window, true);
+ Scrollbar(ImGuiLayoutType_Horizontal);
if (window->ScrollbarY)
- Scrollbar(window, false);
+ Scrollbar(ImGuiLayoutType_Vertical);
// Render resize grip
// (after the input handling so we don't have a frame of latency)
@@ -4316,13 +4532,11 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
window->DC.ChildWindows.resize(0);
window->DC.LayoutType = ImGuiLayoutType_Vertical;
+ window->DC.ItemFlags = ImGuiItemFlags_Default_;
window->DC.ItemWidth = window->ItemWidthDefault;
window->DC.TextWrapPos = -1.0f; // disabled
- window->DC.AllowKeyboardFocus = true;
- window->DC.ButtonRepeat = false;
+ window->DC.ItemFlagsStack.resize(0);
window->DC.ItemWidthStack.resize(0);
- window->DC.AllowKeyboardFocusStack.resize(0);
- window->DC.ButtonRepeatStack.resize(0);
window->DC.TextWrapPosStack.resize(0);
window->DC.ColumnsCurrent = 0;
window->DC.ColumnsCount = 1;
@@ -4332,7 +4546,13 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
window->DC.TreeDepth = 0;
window->DC.StateStorage = &window->StateStorage;
window->DC.GroupStack.resize(0);
- window->MenuColumns.Update(3, style.ItemSpacing.x, !window_was_active);
+ window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
+
+ if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
+ {
+ window->DC.ItemFlags = parent_window->DC.ItemFlags;
+ window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
+ }
if (window->AutoFitFramesX > 0)
window->AutoFitFramesX--;
@@ -4340,25 +4560,30 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
window->AutoFitFramesY--;
// New windows appears in front (we need to do that AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
- if (!window_was_active && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
+ if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
if (!(flags & (ImGuiWindowFlags_ChildWindow|ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
FocusWindow(window);
// Title bar
if (!(flags & ImGuiWindowFlags_NoTitleBar))
{
+ // Collapse button
+ if (!(flags & ImGuiWindowFlags_NoCollapse))
+ {
+ RenderTriangle(window->Pos + style.FramePadding, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);
+ }
+
+ // Close button
if (p_open != NULL)
{
- const float pad = 2.0f;
- const float rad = (window->TitleBarHeight() - pad*2.0f) * 0.5f;
- if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad))
+ const float PAD = 2.0f;
+ const float rad = (window->TitleBarHeight() - PAD*2.0f) * 0.5f;
+ if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-PAD - rad, PAD + rad), rad))
*p_open = false;
}
+ // Title text (FIXME: refactor text alignment facilities along with RenderText helpers)
const ImVec2 text_size = CalcTextSize(name, NULL, true);
- if (!(flags & ImGuiWindowFlags_NoCollapse))
- RenderCollapseTriangle(window->Pos + style.FramePadding, !window->Collapsed, 1.0f);
-
ImVec2 text_min = window->Pos;
ImVec2 text_max = window->Pos + ImVec2(window->Size.x, style.FramePadding.y*2 + text_size.y);
ImRect clip_rect;
@@ -4384,24 +4609,31 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
ImGui::LogToClipboard();
*/
+
+ // Inner rectangle
+ // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
+ // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior.
+ window->InnerRect.Min.x = title_bar_rect.Min.x;
+ window->InnerRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight();
+ window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
+ window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
+ //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
}
// Inner clipping rectangle
- // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
- // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior.
- const ImRect title_bar_rect = window->TitleBarRect();
+ // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
const float border_size = window->BorderSize;
- // Force round to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
ImRect clip_rect;
- clip_rect.Min.x = ImFloor(0.5f + title_bar_rect.Min.x + ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
- clip_rect.Min.y = ImFloor(0.5f + title_bar_rect.Max.y + window->MenuBarHeight() + border_size);
- clip_rect.Max.x = ImFloor(0.5f + window->Pos.x + window->Size.x - window->ScrollbarSizes.x - ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
- clip_rect.Max.y = ImFloor(0.5f + window->Pos.y + window->Size.y - window->ScrollbarSizes.y - border_size);
+ clip_rect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
+ clip_rect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + border_size);
+ clip_rect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f)));
+ clip_rect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - border_size);
PushClipRect(clip_rect.Min, clip_rect.Max, true);
- // Clear 'accessed' flag last thing
+ // 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)
if (first_begin_of_the_frame)
window->Accessed = false;
+
window->BeginCount++;
g.SetNextWindowSizeConstraint = false;
@@ -4428,6 +4660,32 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
return !window->SkipItems;
}
+// Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()+Begin() instead.
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha_override, ImGuiWindowFlags flags)
+{
+ // Old API feature: we could pass the initial window size as a parameter, however this was very misleading because in most cases it would only affect the window when it didn't have storage in the .ini file.
+ if (size_on_first_use.x != 0.0f || size_on_first_use.y != 0.0f)
+ SetNextWindowSize(size_on_first_use, ImGuiCond_FirstUseEver);
+
+ // Old API feature: we could override the window background alpha with a parameter. This is actually tricky to reproduce manually because:
+ // (1) there are multiple variants of WindowBg (popup, tooltip, etc.) and (2) you can't call PushStyleColor before Begin and PopStyleColor just after Begin() because of how CheckStackSizes() behave.
+ // The user-side solution is to do backup = GetStyleColorVec4(ImGuiCol_xxxBG), PushStyleColor(ImGuiCol_xxxBg), Begin, PushStyleColor(ImGuiCol_xxxBg, backup), [...], PopStyleColor(), End(); PopStyleColor() - which is super awkward.
+ // The alpha override was rarely used but for now we'll leave the Begin() variant around for a bit. We may either lift the constraint on CheckStackSizes() either add a SetNextWindowBgAlpha() helper that does it magically.
+ ImGuiContext& g = *GImGui;
+ const ImGuiCol bg_color_idx = GetWindowBgColorIdxFromFlags(flags);
+ const ImVec4 bg_color_backup = g.Style.Colors[bg_color_idx];
+ if (bg_alpha_override >= 0.0f)
+ g.Style.Colors[bg_color_idx].w = bg_alpha_override;
+
+ bool ret = Begin(name, p_open, flags);
+
+ if (bg_alpha_override >= 0.0f)
+ g.Style.Colors[bg_color_idx] = bg_color_backup;
+ return ret;
+}
+#endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+
void ImGui::End()
{
ImGuiContext& g = *GImGui;
@@ -4455,9 +4713,12 @@ void ImGui::End()
// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
// - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar
// - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.
-static void Scrollbar(ImGuiWindow* window, bool horizontal)
+void ImGui::Scrollbar(ImGuiLayoutType direction)
{
ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+
+ const bool horizontal = (direction == ImGuiLayoutType_Horizontal);
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY");
@@ -4477,10 +4738,10 @@ static void Scrollbar(ImGuiWindow* window, bool horizontal)
float window_rounding = (window->Flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding;
int window_rounding_corners;
if (horizontal)
- window_rounding_corners = ImGuiCorner_BottomLeft | (other_scrollbar ? 0 : ImGuiCorner_BottomRight);
+ window_rounding_corners = ImGuiCorner_BotLeft | (other_scrollbar ? 0 : ImGuiCorner_BotRight);
else
- window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImGuiCorner_TopRight : 0) | (other_scrollbar ? 0 : ImGuiCorner_BottomRight);
- window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_ScrollbarBg), window_rounding, window_rounding_corners);
+ window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImGuiCorner_TopRight : 0) | (other_scrollbar ? 0 : ImGuiCorner_BotRight);
+ window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window_rounding, window_rounding_corners);
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)));
// V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
@@ -4500,7 +4761,7 @@ static void Scrollbar(ImGuiWindow* window, bool horizontal)
bool held = false;
bool hovered = false;
const bool previously_held = (g.ActiveId == id);
- ImGui::ButtonBehavior(bb, id, &hovered, &held);
+ ButtonBehavior(bb, id, &hovered, &held);
float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v);
float scroll_ratio = ImSaturate(scroll_v / scroll_max);
@@ -4513,7 +4774,7 @@ static void Scrollbar(ImGuiWindow* window, bool horizontal)
// Click position in scrollbar normalized space (0.0f->1.0f)
const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
- ImGui::SetHoveredID(id);
+ SetHoveredID(id);
bool seek_absolute = false;
if (!previously_held)
@@ -4549,7 +4810,7 @@ static void Scrollbar(ImGuiWindow* window, bool horizontal)
}
// Render
- const ImU32 grab_col = ImGui::GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab);
+ const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab);
if (horizontal)
window->DrawList->AddRectFilled(ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y), ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y), grab_col, style.ScrollbarRounding);
else
@@ -4589,6 +4850,17 @@ void ImGui::FocusWindow(ImGuiWindow* window)
g.Windows.push_back(window);
}
+void ImGui::FocusPreviousWindow()
+{
+ ImGuiContext& g = *GImGui;
+ for (int i = g.Windows.Size - 1; i >= 0; i--)
+ if (g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow))
+ {
+ FocusWindow(g.Windows[i]);
+ return;
+ }
+}
+
void ImGui::PushItemWidth(float item_width)
{
ImGuiWindow* window = GetCurrentWindow();
@@ -4596,12 +4868,12 @@ void ImGui::PushItemWidth(float item_width)
window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
}
-static void PushMultiItemsWidths(int components, float w_full)
+void ImGui::PushMultiItemsWidths(int components, float w_full)
{
- ImGuiWindow* window = ImGui::GetCurrentWindow();
+ ImGuiWindow* window = GetCurrentWindow();
const ImGuiStyle& style = GImGui->Style;
if (w_full <= 0.0f)
- w_full = ImGui::CalcItemWidth();
+ w_full = CalcItemWidth();
const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
window->DC.ItemWidthStack.push_back(w_item_last);
@@ -4666,32 +4938,41 @@ void ImGui::PopFont()
SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
}
-void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
+void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
{
ImGuiWindow* window = GetCurrentWindow();
- window->DC.AllowKeyboardFocus = allow_keyboard_focus;
- window->DC.AllowKeyboardFocusStack.push_back(allow_keyboard_focus);
+ if (enabled)
+ window->DC.ItemFlags |= option;
+ else
+ window->DC.ItemFlags &= ~option;
+ window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
}
-void ImGui::PopAllowKeyboardFocus()
+void ImGui::PopItemFlag()
{
ImGuiWindow* window = GetCurrentWindow();
- window->DC.AllowKeyboardFocusStack.pop_back();
- window->DC.AllowKeyboardFocus = window->DC.AllowKeyboardFocusStack.empty() ? true : window->DC.AllowKeyboardFocusStack.back();
+ window->DC.ItemFlagsStack.pop_back();
+ window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
+}
+
+void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
+{
+ PushItemFlag(ImGuiItemFlags_AllowKeyboardFocus, allow_keyboard_focus);
+}
+
+void ImGui::PopAllowKeyboardFocus()
+{
+ PopItemFlag();
}
void ImGui::PushButtonRepeat(bool repeat)
{
- ImGuiWindow* window = GetCurrentWindow();
- window->DC.ButtonRepeat = repeat;
- window->DC.ButtonRepeatStack.push_back(repeat);
+ PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
}
void ImGui::PopButtonRepeat()
{
- ImGuiWindow* window = GetCurrentWindow();
- window->DC.ButtonRepeatStack.pop_back();
- window->DC.ButtonRepeat = window->DC.ButtonRepeatStack.empty() ? false : window->DC.ButtonRepeatStack.back();
+ PopItemFlag();
}
void ImGui::PushTextWrapPos(float wrap_pos_x)
@@ -4745,7 +5026,7 @@ struct ImGuiStyleVarInfo
{
ImGuiDataType Type;
ImU32 Offset;
- void* GetVarPtr() const { return (void*)((unsigned char*)&GImGui->Style + Offset); }
+ void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
};
static const ImGuiStyleVarInfo GStyleVarInfo[ImGuiStyleVar_Count_] =
@@ -4775,8 +5056,9 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
if (var_info->Type == ImGuiDataType_Float)
{
- float* pvar = (float*)var_info->GetVarPtr();
- GImGui->StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
+ ImGuiContext& g = *GImGui;
+ float* pvar = (float*)var_info->GetVarPtr(&g.Style);
+ g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
*pvar = val;
return;
}
@@ -4788,8 +5070,9 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
if (var_info->Type == ImGuiDataType_Float2)
{
- ImVec2* pvar = (ImVec2*)var_info->GetVarPtr();
- GImGui->StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
+ ImGuiContext& g = *GImGui;
+ ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
+ g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
*pvar = val;
return;
}
@@ -4803,9 +5086,9 @@ void ImGui::PopStyleVar(int count)
{
ImGuiStyleMod& backup = g.StyleModifiers.back();
const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
- if (info->Type == ImGuiDataType_Float) (*(float*)info->GetVarPtr()) = backup.BackupFloat[0];
- else if (info->Type == ImGuiDataType_Float2) (*(ImVec2*)info->GetVarPtr()) = ImVec2(backup.BackupFloat[0], backup.BackupFloat[1]);
- else if (info->Type == ImGuiDataType_Int) (*(int*)info->GetVarPtr()) = backup.BackupInt[0];
+ if (info->Type == ImGuiDataType_Float) (*(float*)info->GetVarPtr(&g.Style)) = backup.BackupFloat[0];
+ else if (info->Type == ImGuiDataType_Float2) (*(ImVec2*)info->GetVarPtr(&g.Style)) = ImVec2(backup.BackupFloat[0], backup.BackupFloat[1]);
+ else if (info->Type == ImGuiDataType_Int) (*(int*)info->GetVarPtr(&g.Style)) = backup.BackupInt[0];
g.StyleModifiers.pop_back();
count--;
}
@@ -4827,8 +5110,8 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx)
case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
case ImGuiCol_FrameBgActive: return "FrameBgActive";
case ImGuiCol_TitleBg: return "TitleBg";
- case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
case ImGuiCol_TitleBgActive: return "TitleBgActive";
+ case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
case ImGuiCol_MenuBarBg: return "MenuBarBg";
case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
@@ -4864,16 +5147,18 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx)
return "Unknown";
}
-bool ImGui::IsWindowHovered()
-{
- ImGuiContext& g = *GImGui;
- return g.HoveredWindow == g.CurrentWindow && IsWindowContentHoverable(g.HoveredRootWindow);
-}
-
-bool ImGui::IsWindowRectHovered()
+bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
{
+ IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
ImGuiContext& g = *GImGui;
- return g.HoveredWindow == g.CurrentWindow;
+ if (g.HoveredWindow != g.CurrentWindow)
+ return false;
+ if (!IsWindowContentHoverable(g.HoveredRootWindow, flags))
+ return false;
+ if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
+ if (g.ActiveId != 0 && g.ActiveIdWindow != g.CurrentWindow)
+ return false;
+ return true;
}
bool ImGui::IsWindowFocused()
@@ -4894,10 +5179,18 @@ bool ImGui::IsRootWindowOrAnyChildFocused()
return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
}
-bool ImGui::IsRootWindowOrAnyChildHovered()
+bool ImGui::IsRootWindowOrAnyChildHovered(ImGuiHoveredFlags flags)
{
+ IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
ImGuiContext& g = *GImGui;
- return g.HoveredRootWindow && (g.HoveredRootWindow == g.CurrentWindow->RootWindow) && IsWindowContentHoverable(g.HoveredRootWindow);
+ if (!g.HoveredRootWindow || (g.HoveredRootWindow != g.CurrentWindow->RootWindow))
+ return false;
+ if (!IsWindowContentHoverable(g.HoveredRootWindow, flags))
+ return false;
+ if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
+ if (g.ActiveId != 0 && g.ActiveIdWindow != g.CurrentWindow)
+ return false;
+ return true;
}
float ImGui::GetWindowWidth()
@@ -4932,7 +5225,7 @@ static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
return;
window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
- window->SetWindowPosCenterWanted = false;
+ window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
// Set
const ImVec2 old_pos = window->Pos;
@@ -4997,8 +5290,7 @@ void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
{
- ImGuiWindow* window = FindWindowByName(name);
- if (window)
+ if (ImGuiWindow* window = FindWindowByName(name))
SetWindowSize(window, size, cond);
}
@@ -5020,7 +5312,14 @@ void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
bool ImGui::IsWindowCollapsed()
{
- return GImGui->CurrentWindow->Collapsed;
+ ImGuiWindow* window = GetCurrentWindowRead();
+ return window->Collapsed;
+}
+
+bool ImGui::IsWindowAppearing()
+{
+ ImGuiWindow* window = GetCurrentWindowRead();
+ return window->Appearing;
}
void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
@@ -5048,17 +5347,11 @@ void ImGui::SetWindowFocus(const char* name)
}
}
-void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond)
+void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
{
ImGuiContext& g = *GImGui;
g.SetNextWindowPosVal = pos;
- g.SetNextWindowPosCond = cond ? cond : ImGuiCond_Always;
-}
-
-void ImGui::SetNextWindowPosCenter(ImGuiCond cond)
-{
- ImGuiContext& g = *GImGui;
- g.SetNextWindowPosVal = ImVec2(-FLT_MAX, -FLT_MAX);
+ g.SetNextWindowPosPivot = pivot;
g.SetNextWindowPosCond = cond ? cond : ImGuiCond_Always;
}
@@ -5310,6 +5603,7 @@ void ImGui::SetScrollHere(float center_y_ratio)
void ImGui::SetKeyboardFocusHere(int offset)
{
+ IM_ASSERT(offset >= -1); // -1 is allowed but not below
ImGuiWindow* window = GetCurrentWindow();
window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
window->FocusIdxTabRequestNext = INT_MAX;
@@ -5404,6 +5698,7 @@ void ImGui::TextUnformatted(const char* text, const char* text_end)
if (text_end == NULL)
text_end = text + strlen(text); // FIXME-OPT
+ const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset);
const float wrap_pos_x = window->DC.TextWrapPos;
const bool wrap_enabled = wrap_pos_x >= 0.0f;
if (text_end - text > 2000 && !wrap_enabled)
@@ -5414,7 +5709,6 @@ void ImGui::TextUnformatted(const char* text, const char* text_end)
// 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.
const char* line = text;
const float line_height = GetTextLineHeight();
- const ImVec2 text_pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrentLineTextBaseOffset);
const ImRect clip_rect = window->ClipRect;
ImVec2 text_size(0,0);
@@ -5448,7 +5742,7 @@ void ImGui::TextUnformatted(const char* text, const char* text_end)
while (line < text_end)
{
const char* line_end = strchr(line, '\n');
- if (IsClippedEx(line_rect, NULL, false))
+ if (IsClippedEx(line_rect, 0, false))
break;
const ImVec2 line_size = CalcTextSize(line, line_end, false);
@@ -5480,7 +5774,7 @@ void ImGui::TextUnformatted(const char* text, const char* text_end)
ImRect bb(text_pos, text_pos + text_size);
ItemSize(bb);
- ItemAdd(bb, NULL);
+ ItemAdd(bb, 0);
}
else
{
@@ -5488,10 +5782,9 @@ void ImGui::TextUnformatted(const char* text, const char* text_end)
const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);
// Account of baseline offset
- ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset);
ImRect bb(text_pos, text_pos + text_size);
ItemSize(text_size);
- if (!ItemAdd(bb, NULL))
+ if (!ItemAdd(bb, 0))
return;
// Render (we don't hide text after ## in this end-user function)
@@ -5499,16 +5792,15 @@ void ImGui::TextUnformatted(const char* text, const char* text_end)
}
}
-void ImGui::AlignFirstTextHeightToWidgets()
+void ImGui::AlignTextToFramePadding()
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
- // Declare a dummy item size to that upcoming items that are smaller will center-align on the newly expanded line height.
ImGuiContext& g = *GImGui;
- ItemSize(ImVec2(0, g.FontSize + g.Style.FramePadding.y*2), g.Style.FramePadding.y);
- SameLine(0, 0);
+ window->DC.CurrentLineHeight = ImMax(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y * 2);
+ window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
}
// Add a label+text combo aligned to other label+value widgets
@@ -5526,7 +5818,7 @@ void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2));
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);
ItemSize(total_bb, style.FramePadding.y);
- if (!ItemAdd(total_bb, NULL))
+ if (!ItemAdd(total_bb, 0))
return;
// Render
@@ -5545,19 +5837,6 @@ void ImGui::LabelText(const char* label, const char* fmt, ...)
va_end(args);
}
-static inline bool IsWindowContentHoverable(ImGuiWindow* window)
-{
- // An active popup disable hovering on other windows (apart from its own children)
- // FIXME-OPT: This could be cached/stored within the window.
- ImGuiContext& g = *GImGui;
- if (g.NavWindow)
- if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
- if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) != 0 && focused_root_window->WasActive && focused_root_window != window->RootWindow)
- return false;
-
- return true;
-}
-
bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)
{
ImGuiContext& g = *GImGui;
@@ -5575,11 +5854,18 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0)
flags |= ImGuiButtonFlags_PressedOnClickRelease;
+ ImGuiWindow* backup_hovered_window = g.HoveredWindow;
+ if ((flags & ImGuiButtonFlags_FlattenChilds) && g.HoveredRootWindow == window)
+ g.HoveredWindow = window;
+
bool pressed = false;
- bool hovered = IsHovered(bb, id, (flags & ImGuiButtonFlags_FlattenChilds) != 0);
+ bool hovered = ItemHoverable(bb, id);
+
+ if ((flags & ImGuiButtonFlags_FlattenChilds) && g.HoveredRootWindow == window)
+ g.HoveredWindow = backup_hovered_window;
+
if (hovered)
{
- SetHoveredID(id);
if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))
{
// | CLICKING | HOLDING with ImGuiButtonFlags_Repeat
@@ -5596,8 +5882,12 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0]))
{
pressed = true;
- ClearActiveID();
+ if (flags & ImGuiButtonFlags_NoHoldingActiveID)
+ ClearActiveID();
+ else
+ SetActiveID(id, window); // Hold on ID
FocusWindow(window);
+ g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;
}
if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0])
{
@@ -5657,10 +5947,10 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags
const ImRect bb(pos, pos + size);
ItemSize(bb, style.FramePadding.y);
- if (!ItemAdd(bb, &id))
+ if (!ItemAdd(bb, id))
return false;
- if (window->DC.ButtonRepeat) flags |= ImGuiButtonFlags_Repeat;
+ if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) flags |= ImGuiButtonFlags_Repeat;
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
@@ -5704,7 +5994,7 @@ bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg)
ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
ItemSize(bb);
- if (!ItemAdd(bb, &id))
+ if (!ItemAdd(bb, id))
return false;
bool hovered, held;
@@ -5748,7 +6038,7 @@ void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2&
if (border_col.w > 0.0f)
bb.Max += ImVec2(2,2);
ItemSize(bb);
- if (!ItemAdd(bb, NULL))
+ if (!ItemAdd(bb, 0))
return;
if (border_col.w > 0.0f)
@@ -5785,7 +6075,7 @@ bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const I
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding*2);
const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size);
ItemSize(bb);
- if (!ItemAdd(bb, &id))
+ if (!ItemAdd(bb, id))
return false;
bool hovered, held;
@@ -5807,7 +6097,7 @@ void ImGui::LogToTTY(int max_depth)
ImGuiContext& g = *GImGui;
if (g.LogEnabled)
return;
- ImGuiWindow* window = GetCurrentWindowRead();
+ ImGuiWindow* window = g.CurrentWindow;
g.LogEnabled = true;
g.LogFile = stdout;
@@ -5822,7 +6112,7 @@ void ImGui::LogToFile(int max_depth, const char* filename)
ImGuiContext& g = *GImGui;
if (g.LogEnabled)
return;
- ImGuiWindow* window = GetCurrentWindowRead();
+ ImGuiWindow* window = g.CurrentWindow;
if (!filename)
{
@@ -5849,7 +6139,7 @@ void ImGui::LogToClipboard(int max_depth)
ImGuiContext& g = *GImGui;
if (g.LogEnabled)
return;
- ImGuiWindow* window = GetCurrentWindowRead();
+ ImGuiWindow* window = g.CurrentWindow;
g.LogEnabled = true;
g.LogFile = NULL;
@@ -5962,14 +6252,14 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
- const ImVec2 padding = display_frame ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
+ const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
if (!label_end)
label_end = FindRenderedTextEnd(label);
const ImVec2 label_size = CalcTextSize(label, label_end, false);
// We vertically grow up to current line height up the typical widget height.
- const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset - padding.y); // Latch before ItemSize changes it
+ const float text_base_offset_y = ImMax(padding.y, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2);
ImRect bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height));
if (display_frame)
@@ -5984,10 +6274,10 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
ItemSize(ImVec2(text_width, frame_height), text_base_offset_y);
// For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
- // (Ideally we'd want to add a flag for the user to specify we want want the hit test to be done up to the right side of the content or not)
+ // (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)
const ImRect interact_bb = display_frame ? bb : ImRect(bb.Min.x, bb.Min.y, bb.Min.x + text_width + style.ItemSpacing.x*2, bb.Max.y);
bool is_open = TreeNodeBehaviorIsOpen(id, flags);
- if (!ItemAdd(interact_bb, &id))
+ if (!ItemAdd(interact_bb, id))
{
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
TreePushRawID(id);
@@ -6021,20 +6311,20 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
// Render
const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
- const ImVec2 text_pos = bb.Min + ImVec2(text_offset_x, padding.y + text_base_offset_y);
+ const ImVec2 text_pos = bb.Min + ImVec2(text_offset_x, text_base_offset_y);
if (display_frame)
{
// Framed type
RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
- RenderCollapseTriangle(bb.Min + padding + ImVec2(0.0f, text_base_offset_y), is_open, 1.0f);
+ RenderTriangle(bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f);
if (g.LogEnabled)
{
// 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.
const char log_prefix[] = "\n##";
const char log_suffix[] = "##";
- LogRenderedText(text_pos, log_prefix, log_prefix+3);
+ LogRenderedText(&text_pos, log_prefix, log_prefix+3);
RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size);
- LogRenderedText(text_pos, log_suffix+1, log_suffix+3);
+ LogRenderedText(&text_pos, log_suffix+1, log_suffix+3);
}
else
{
@@ -6050,9 +6340,9 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
if (flags & ImGuiTreeNodeFlags_Bullet)
RenderBullet(bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y));
else if (!(flags & ImGuiTreeNodeFlags_Leaf))
- RenderCollapseTriangle(bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open, 0.70f);
+ RenderTriangle(bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f);
if (g.LogEnabled)
- LogRenderedText(text_pos, ">");
+ LogRenderedText(&text_pos, ">");
RenderText(text_pos, label, label_end, false);
}
@@ -6088,8 +6378,11 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags
// Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
ImGuiContext& g = *GImGui;
float button_sz = g.FontSize * 0.5f;
+ ImGuiItemHoveredDataBackup last_item_backup;
+ last_item_backup.Backup();
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))
*p_open = false;
+ last_item_backup.Restore();
}
return is_open;
@@ -6196,6 +6489,8 @@ float ImGui::GetTreeNodeToLabelSpacing()
void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond)
{
ImGuiContext& g = *GImGui;
+ if (g.CurrentWindow->SkipItems)
+ return;
g.SetNextTreeNodeOpenVal = is_open;
g.SetNextTreeNodeOpenCond = cond ? cond : ImGuiCond_Always;
}
@@ -6257,7 +6552,7 @@ void ImGui::Bullet()
const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));
ItemSize(bb);
- if (!ItemAdd(bb, NULL))
+ if (!ItemAdd(bb, 0))
{
SameLine(0, style.FramePadding.x*2);
return;
@@ -6285,7 +6580,7 @@ void ImGui::BulletTextV(const char* fmt, va_list args)
const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize);
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
ItemSize(bb);
- if (!ItemAdd(bb, NULL))
+ if (!ItemAdd(bb, 0))
return;
// Render
@@ -6416,6 +6711,7 @@ bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label
ImGuiWindow* window = GetCurrentWindow();
// Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen)
+ // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id
SetActiveID(g.ScalarAsInputTextId, window);
SetHoveredID(0);
FocusableItemUnregister(window);
@@ -6429,11 +6725,6 @@ bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label
g.ScalarAsInputTextId = g.ActiveId;
SetHoveredID(id);
}
- else if (g.ActiveId != g.ScalarAsInputTextId)
- {
- // Release
- g.ScalarAsInputTextId = 0;
- }
if (text_value_changed)
return DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, NULL);
return false;
@@ -6451,10 +6742,12 @@ int ImGui::ParseFormatPrecision(const char* fmt, int default_precision)
fmt++;
if (*fmt == '.')
{
- precision = atoi(fmt + 1);
+ fmt = ImAtoi(fmt + 1, &precision);
if (precision < 0 || precision > 10)
precision = default_precision;
}
+ if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation
+ precision = -1;
break;
}
return precision;
@@ -6471,6 +6764,8 @@ float ImGui::RoundScalar(float value, int decimal_precision)
// Round past decimal precision
// So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0
// FIXME: Investigate better rounding methods
+ if (decimal_precision < 0)
+ return value;
const float min_step = GetMinimumStepAtDecimalPrecision(decimal_precision);
bool negative = value < 0.0f;
value = fabsf(value);
@@ -6522,7 +6817,7 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v
const float grab_padding = 2.0f;
const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f);
float grab_sz;
- if (decimal_precision > 0)
+ if (decimal_precision != 0)
grab_sz = ImMin(style.GrabMinSize, slider_sz);
else
grab_sz = ImMin(ImMax(1.0f * (slider_sz / ((v_min < v_max ? v_max - v_min : v_min - v_max) + 1.0f)), style.GrabMinSize), slider_sz); // Integer sliders, if possible have the grab size represent 1 unit
@@ -6641,15 +6936,12 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c
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));
// NB- we don't call ItemSize() yet because we may turn into a text edit box below
- if (!ItemAdd(total_bb, &id))
+ if (!ItemAdd(total_bb, id))
{
ItemSize(total_bb, style.FramePadding.y);
return false;
}
-
- const bool hovered = IsHovered(frame_bb, id);
- if (hovered)
- SetHoveredID(id);
+ const bool hovered = ItemHoverable(frame_bb, id);
if (!display_format)
display_format = "%.3f";
@@ -6657,12 +6949,11 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c
// Tabbing or CTRL-clicking on Slider turns it into an input box
bool start_text_input = false;
- const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id);
+ const bool tab_focus_requested = FocusableItemRegister(window, id);
if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]))
{
SetActiveID(id, window);
FocusWindow(window);
-
if (tab_focus_requested || g.IO.KeyCtrl)
{
start_text_input = true;
@@ -6672,9 +6963,8 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c
if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id))
return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision);
- ItemSize(total_bb, style.FramePadding.y);
-
// Actual slider behavior + render grab
+ ItemSize(total_bb, style.FramePadding.y);
const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision);
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
@@ -6703,12 +6993,9 @@ bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float
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));
ItemSize(bb, style.FramePadding.y);
- if (!ItemAdd(frame_bb, &id))
+ if (!ItemAdd(frame_bb, id))
return false;
-
- const bool hovered = IsHovered(frame_bb, id);
- if (hovered)
- SetHoveredID(id);
+ const bool hovered = ItemHoverable(frame_bb, id);
if (!display_format)
display_format = "%.3f";
@@ -6872,17 +7159,23 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s
if (v_speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX)
v_speed = (v_max - v_min) * g.DragSpeedDefaultRatio;
+
float v_cur = g.DragCurrentValue;
const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f);
- if (fabsf(mouse_drag_delta.x - g.DragLastMouseDelta.x) > 0.0f)
+ float adjust_delta = 0.0f;
+ //if (g.ActiveIdSource == ImGuiInputSource_Mouse)
{
- float speed = v_speed;
+ adjust_delta = mouse_drag_delta.x - g.DragLastMouseDelta.x;
if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f)
- speed = speed * g.DragSpeedScaleFast;
+ adjust_delta *= g.DragSpeedScaleFast;
if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f)
- speed = speed * g.DragSpeedScaleSlow;
+ adjust_delta *= g.DragSpeedScaleSlow;
+ }
+ adjust_delta *= v_speed;
+ g.DragLastMouseDelta.x = mouse_drag_delta.x;
- float adjust_delta = (mouse_drag_delta.x - g.DragLastMouseDelta.x) * speed;
+ if (fabsf(adjust_delta) > 0.0f)
+ {
if (fabsf(power - 1.0f) > 0.001f)
{
// Logarithmic curve on both side of 0.0
@@ -6897,7 +7190,6 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s
{
v_cur += adjust_delta;
}
- g.DragLastMouseDelta.x = mouse_drag_delta.x;
// Clamp
if (v_min < v_max)
@@ -6939,15 +7231,12 @@ bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, f
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));
// NB- we don't call ItemSize() yet because we may turn into a text edit box below
- if (!ItemAdd(total_bb, &id))
+ if (!ItemAdd(total_bb, id))
{
ItemSize(total_bb, style.FramePadding.y);
return false;
}
-
- const bool hovered = IsHovered(frame_bb, id);
- if (hovered)
- SetHoveredID(id);
+ const bool hovered = ItemHoverable(frame_bb, id);
if (!display_format)
display_format = "%.3f";
@@ -6955,12 +7244,11 @@ bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, f
// Tabbing or CTRL-clicking on Drag turns it into an input box
bool start_text_input = false;
- const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id);
- if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] | g.IO.MouseDoubleClicked[0])))
+ const bool tab_focus_requested = FocusableItemRegister(window, id);
+ if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])))
{
SetActiveID(id, window);
FocusWindow(window);
-
if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0])
{
start_text_input = true;
@@ -7149,8 +7437,9 @@ void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_ge
const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
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));
ItemSize(total_bb, style.FramePadding.y);
- if (!ItemAdd(total_bb, NULL))
+ if (!ItemAdd(total_bb, 0))
return;
+ const bool hovered = ItemHoverable(inner_bb, 0);
// Determine scale from values if not specified
if (scale_min == FLT_MAX || scale_max == FLT_MAX)
@@ -7178,7 +7467,7 @@ void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_ge
// Tooltip on hover
int v_hovered = -1;
- if (IsHovered(inner_bb, 0))
+ if (hovered)
{
const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
const int v_idx = (int)(t * item_count);
@@ -7288,7 +7577,7 @@ void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* over
ImVec2 pos = window->DC.CursorPos;
ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f));
ItemSize(bb, style.FramePadding.y);
- if (!ItemAdd(bb, NULL))
+ if (!ItemAdd(bb, 0))
return;
// Render
@@ -7296,7 +7585,7 @@ void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* over
RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
bb.Expand(ImVec2(-window->BorderSize, -window->BorderSize));
const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y);
- RenderFrame(bb.Min, fill_br, GetColorU32(ImGuiCol_PlotHistogram), false, style.FrameRounding);
+ RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding);
// Default displaying the fraction as percentage string, but user can override it
char overlay_buf[32];
@@ -7335,7 +7624,7 @@ bool ImGui::Checkbox(const char* label, bool* v)
total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max));
}
- if (!ItemAdd(total_bb, &id))
+ if (!ItemAdd(total_bb, id))
return false;
bool hovered, held;
@@ -7348,13 +7637,13 @@ bool ImGui::Checkbox(const char* label, bool* v)
{
const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight());
const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f));
- window->DrawList->AddRectFilled(check_bb.Min+ImVec2(pad,pad), check_bb.Max-ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), style.FrameRounding);
+ RenderCheckMark(check_bb.Min + ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), check_bb.GetWidth() - pad*2.0f);
}
if (g.LogEnabled)
- LogRenderedText(text_bb.GetTL(), *v ? "[x]" : "[ ]");
+ LogRenderedText(&text_bb.Min, *v ? "[x]" : "[ ]");
if (label_size.x > 0.0f)
- RenderText(text_bb.GetTL(), label);
+ RenderText(text_bb.Min, label);
return pressed;
}
@@ -7398,7 +7687,7 @@ bool ImGui::RadioButton(const char* label, bool active)
total_bb.Add(text_bb);
}
- if (!ItemAdd(total_bb, &id))
+ if (!ItemAdd(total_bb, id))
return false;
ImVec2 center = check_bb.GetCenter();
@@ -7424,9 +7713,9 @@ bool ImGui::RadioButton(const char* label, bool active)
}
if (g.LogEnabled)
- LogRenderedText(text_bb.GetTL(), active ? "(x)" : "( )");
+ LogRenderedText(&text_bb.Min, active ? "(x)" : "( )");
if (label_size.x > 0.0f)
- RenderText(text_bb.GetTL(), label);
+ RenderText(text_bb.Min, label);
return pressed;
}
@@ -7736,14 +8025,17 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
else
{
ItemSize(total_bb, style.FramePadding.y);
- if (!ItemAdd(total_bb, &id))
+ if (!ItemAdd(total_bb, id))
return false;
}
+ const bool hovered = ItemHoverable(frame_bb, id);
+ if (hovered)
+ g.MouseCursor = ImGuiMouseCursor_TextInput;
// Password pushes a temporary font with only a fallback glyph
if (is_password)
{
- const ImFont::Glyph* glyph = g.Font->FindGlyph('*');
+ const ImFontGlyph* glyph = g.Font->FindGlyph('*');
ImFont* password_font = &g.InputTextPasswordFont;
password_font->FontSize = g.Font->FontSize;
password_font->Scale = g.Font->Scale;
@@ -7752,27 +8044,23 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
password_font->Descent = g.Font->Descent;
password_font->ContainerAtlas = g.Font->ContainerAtlas;
password_font->FallbackGlyph = glyph;
- password_font->FallbackXAdvance = glyph->XAdvance;
- IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexXAdvance.empty() && password_font->IndexLookup.empty());
+ password_font->FallbackAdvanceX = glyph->AdvanceX;
+ IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty());
PushFont(password_font);
}
// NB: we are only allowed to access 'edit_state' if we are the active widget.
ImGuiTextEditState& edit_state = g.InputTextState;
- const bool focus_requested = FocusableItemRegister(window, g.ActiveId == id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing
+ const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing
const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent);
const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code;
- const bool hovered = IsHovered(frame_bb, id);
- if (hovered)
- {
- SetHoveredID(id);
- g.MouseCursor = ImGuiMouseCursor_TextInput;
- }
const bool user_clicked = hovered && io.MouseClicked[0];
const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY");
+ bool clear_active_id = false;
+
bool select_all = (g.ActiveId != id) && (flags & ImGuiInputTextFlags_AutoSelectAll) != 0;
if (focus_requested || user_clicked || user_scrolled)
{
@@ -7818,8 +8106,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
else if (io.MouseClicked[0])
{
// Release focus when we click outside
- if (g.ActiveId == id)
- ClearActiveID();
+ clear_active_id = true;
}
bool value_changed = false;
@@ -7842,6 +8129,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
// Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
// Down the line we should have a cleaner library-wide concept of Selected vs Active.
g.ActiveIdAllowOverlap = !io.MouseDown[0];
+ g.WantTextInputNextFrame = 1;
// Edit in progress
const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX;
@@ -7892,9 +8180,12 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
// Consume characters
memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
}
+ }
- // Handle various key-presses
- bool cancel_edit = false;
+ bool cancel_edit = false;
+ if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id)
+ {
+ // Handle key-presses
const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
const bool is_shortcut_key_only = (io.OSXBehaviors ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl
const bool is_wordmove_key_down = io.OSXBehaviors ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl
@@ -7921,8 +8212,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;
if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl))
{
- ClearActiveID();
- enter_pressed = true;
+ enter_pressed = clear_active_id = true;
}
else if (is_editable)
{
@@ -7937,10 +8227,10 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
if (InputTextFilterCharacter(&c, flags, callback, user_data))
edit_state.OnKeyPressed((int)c);
}
- else if (IsKeyPressedMap(ImGuiKey_Escape)) { ClearActiveID(); cancel_edit = true; }
- else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); }
- else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); }
- else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_A)) { edit_state.SelectAll(); edit_state.CursorFollow = true; }
+ else if (IsKeyPressedMap(ImGuiKey_Escape)) { clear_active_id = cancel_edit = true; }
+ else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); }
+ else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); }
+ else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_A)) { edit_state.SelectAll(); edit_state.CursorFollow = true; }
else if (is_shortcut_key_only && !is_password && ((IsKeyPressedMap(ImGuiKey_X) && is_editable) || IsKeyPressedMap(ImGuiKey_C)) && (!is_multiline || edit_state.HasSelection()))
{
// Cut, Copy
@@ -7991,7 +8281,10 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
ImGui::MemFree(clipboard_filtered);
}
}
+ }
+ if (g.ActiveId == id)
+ {
if (cancel_edit)
{
// Restore initial value
@@ -8001,7 +8294,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
value_changed = true;
}
}
- else
+
+ // 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.
+ // 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.
+ bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0);
+ if (apply_edit_back_to_user_buffer)
{
// Apply new value immediately - copy modified buffer back
// Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer
@@ -8089,6 +8386,10 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
}
}
+ // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value)
+ if (clear_active_id && g.ActiveId == id)
+ ClearActiveID();
+
// Render
// 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.
const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL;
@@ -8257,7 +8558,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
// Log as text
if (g.LogEnabled && !is_password)
- LogRenderedText(render_pos, buf_display, NULL);
+ LogRenderedText(&render_pos, buf_display, NULL);
if (label_size.x > 0)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
@@ -8279,6 +8580,12 @@ bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, co
return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);
}
+static inline float SmallSquareSize()
+{
+ ImGuiContext& g = *GImGui;
+ return g.FontSize + g.Style.FramePadding.y * 2.0f;
+}
+
// NB: scalar_format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "display_format" argument)
bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags)
{
@@ -8292,7 +8599,7 @@ bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data
BeginGroup();
PushID(label);
- const ImVec2 button_sz = ImVec2(g.FontSize, g.FontSize) + style.FramePadding*2.0f;
+ const ImVec2 button_sz = ImVec2(SmallSquareSize(), SmallSquareSize());
if (step_ptr)
PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_sz.x + style.ItemInnerSpacing.x)*2));
@@ -8374,7 +8681,6 @@ bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal
}
PopID();
- window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
TextUnformatted(label, FindRenderedTextEnd(label));
EndGroup();
@@ -8417,7 +8723,6 @@ bool ImGui::InputIntN(const char* label, int* v, int components, ImGuiInputTextF
}
PopID();
- window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y);
TextUnformatted(label, FindRenderedTextEnd(label));
EndGroup();
@@ -8488,8 +8793,7 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa
return value_changed;
}
-// Combo box function.
-bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
+bool ImGui::BeginCombo(const char* label, const char* preview_value, ImVec2 popup_size)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
@@ -8504,100 +8808,112 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f));
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));
ItemSize(total_bb, style.FramePadding.y);
- if (!ItemAdd(total_bb, &id))
+ if (!ItemAdd(total_bb, id))
return false;
- const float arrow_size = (g.FontSize + style.FramePadding.x * 2.0f);
- const bool hovered = IsHovered(frame_bb, id);
+ const float arrow_size = SmallSquareSize();
+
+ bool hovered, held;
+ bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
+
bool popup_open = IsPopupOpen(id);
const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_open || hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING
- RenderCollapseTriangle(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y) + style.FramePadding, true);
+ RenderTriangle(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down);
- if (*current_item >= 0 && *current_item < items_count)
- {
- const char* item_text;
- if (items_getter(data, *current_item, &item_text))
- RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, item_text, NULL, NULL, ImVec2(0.0f,0.0f));
- }
+ if (preview_value != NULL)
+ RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f,0.0f));
if (label_size.x > 0)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
- bool popup_toggled = false;
- if (hovered)
+ if (pressed && !popup_open)
{
- SetHoveredID(id);
- if (g.IO.MouseClicked[0])
- {
- ClearActiveID();
- popup_toggled = true;
- }
+ OpenPopupEx(id, false);
+ popup_open = true;
}
- if (popup_toggled)
+
+ if (!popup_open)
+ return false;
+
+ if (popup_size.x == 0.0f)
+ popup_size.x = w;
+
+ float popup_y1 = frame_bb.Max.y;
+ float popup_y2 = ImClamp(popup_y1 + popup_size.y, popup_y1, g.IO.DisplaySize.y - style.DisplaySafeAreaPadding.y);
+ if ((popup_y2 - popup_y1) < ImMin(popup_size.y, frame_bb.Min.y - style.DisplaySafeAreaPadding.y))
{
- if (IsPopupOpen(id))
- {
- ClosePopup(id);
- }
- else
- {
- FocusWindow(window);
- OpenPopup(label);
- popup_open = true;
- }
+ // Position our combo ABOVE because there's more space to fit! (FIXME: Handle in Begin() or use a shared helper. We have similar code in Begin() for popup placement)
+ popup_y1 = ImClamp(frame_bb.Min.y - popup_size.y, style.DisplaySafeAreaPadding.y, frame_bb.Min.y);
+ popup_y2 = frame_bb.Min.y;
+ SetNextWindowPos(ImVec2(frame_bb.Min.x, frame_bb.Min.y), ImGuiCond_Always, ImVec2(0.0f, 1.0f));
+ }
+ else
+ {
+ // Position our combo below
+ SetNextWindowPos(ImVec2(frame_bb.Min.x, frame_bb.Max.y), ImGuiCond_Always, ImVec2(0.0f, 0.0f));
}
+ SetNextWindowSize(ImVec2(popup_size.x, popup_y2 - popup_y1), ImGuiCond_Appearing);
+ PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
- bool value_changed = false;
- if (IsPopupOpen(id))
+ const ImGuiWindowFlags flags = ImGuiWindowFlags_ComboBox | ((window->Flags & ImGuiWindowFlags_ShowBorders) ? ImGuiWindowFlags_ShowBorders : 0);
+ if (!BeginPopupEx(id, flags))
{
- // Size default to hold ~7 items
- if (height_in_items < 0)
- height_in_items = 7;
+ IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above
+ return false;
+ }
+ Spacing();
- float popup_height = (label_size.y + style.ItemSpacing.y) * ImMin(items_count, height_in_items) + (style.FramePadding.y * 3);
- float popup_y1 = frame_bb.Max.y;
- float popup_y2 = ImClamp(popup_y1 + popup_height, popup_y1, g.IO.DisplaySize.y - style.DisplaySafeAreaPadding.y);
- if ((popup_y2 - popup_y1) < ImMin(popup_height, frame_bb.Min.y - style.DisplaySafeAreaPadding.y))
- {
- // Position our combo ABOVE because there's more space to fit! (FIXME: Handle in Begin() or use a shared helper. We have similar code in Begin() for popup placement)
- popup_y1 = ImClamp(frame_bb.Min.y - popup_height, style.DisplaySafeAreaPadding.y, frame_bb.Min.y);
- popup_y2 = frame_bb.Min.y;
- }
- ImRect popup_rect(ImVec2(frame_bb.Min.x, popup_y1), ImVec2(frame_bb.Max.x, popup_y2));
- SetNextWindowPos(popup_rect.Min);
- SetNextWindowSize(popup_rect.GetSize());
- PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
+ return true;
+}
+
+void ImGui::EndCombo()
+{
+ EndPopup();
+ PopStyleVar();
+}
+
+// Combo box function.
+bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
+{
+ ImGuiContext& g = *GImGui;
+ const ImGuiStyle& style = g.Style;
- const ImGuiWindowFlags flags = ImGuiWindowFlags_ComboBox | ((window->Flags & ImGuiWindowFlags_ShowBorders) ? ImGuiWindowFlags_ShowBorders : 0);
- if (BeginPopupEx(id, flags))
+ const char* preview_text = NULL;
+ if (*current_item >= 0 && *current_item < items_count)
+ items_getter(data, *current_item, &preview_text);
+
+ // Size default to hold ~7 items
+ if (height_in_items < 0)
+ height_in_items = 7;
+ float popup_height = (g.FontSize + style.ItemSpacing.y) * ImMin(items_count, height_in_items) + (style.FramePadding.y * 3);
+
+ if (!BeginCombo(label, preview_text, ImVec2(0.0f, popup_height)))
+ return false;
+
+ // Display items
+ // FIXME-OPT: Use clipper
+ bool value_changed = false;
+ for (int i = 0; i < items_count; i++)
+ {
+ PushID((void*)(intptr_t)i);
+ const bool item_selected = (i == *current_item);
+ const char* item_text;
+ if (!items_getter(data, i, &item_text))
+ item_text = "*Unknown item*";
+ if (Selectable(item_text, item_selected))
{
- // Display items
- // FIXME-OPT: Use clipper
- Spacing();
- for (int i = 0; i < items_count; i++)
- {
- PushID((void*)(intptr_t)i);
- const bool item_selected = (i == *current_item);
- const char* item_text;
- if (!items_getter(data, i, &item_text))
- item_text = "*Unknown item*";
- if (Selectable(item_text, item_selected))
- {
- ClearActiveID();
- value_changed = true;
- *current_item = i;
- }
- if (item_selected && popup_toggled)
- SetScrollHere();
- PopID();
- }
- EndPopup();
+ value_changed = true;
+ *current_item = i;
}
- PopStyleVar();
+ if (item_selected && IsWindowAppearing())
+ SetScrollHere();
+ PopID();
}
+
+ EndCombo();
return value_changed;
}
@@ -8641,7 +8957,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
bb_with_spacing.Min.y -= spacing_U;
bb_with_spacing.Max.x += spacing_R;
bb_with_spacing.Max.y += spacing_D;
- if (!ItemAdd(bb_with_spacing, &id))
+ if (!ItemAdd(bb_with_spacing, id))
{
if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1)
PushColumnClipRect();
@@ -8649,8 +8965,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
}
ImGuiButtonFlags button_flags = 0;
- if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick;
- if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnClick|ImGuiButtonFlags_PressedOnRelease;
+ if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_NoHoldingActiveID;
+ if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnRelease;
if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled;
if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
bool hovered, held;
@@ -8676,7 +8992,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor();
// Automatically close popups
- if (pressed && !(flags & ImGuiSelectableFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
+ if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup))
CloseCurrentPopup();
return pressed;
}
@@ -8791,23 +9107,38 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, boo
return false;
ImGuiContext& g = *GImGui;
+ ImGuiStyle& style = g.Style;
ImVec2 pos = window->DC.CursorPos;
ImVec2 label_size = CalcTextSize(label, NULL, true);
- ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
- float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame
- float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
- bool pressed = Selectable(label, false, ImGuiSelectableFlags_MenuItem | ImGuiSelectableFlags_DrawFillAvailWidth | (enabled ? 0 : ImGuiSelectableFlags_Disabled), ImVec2(w, 0.0f));
- if (shortcut_size.x > 0.0f)
+ ImGuiSelectableFlags flags = ImGuiSelectableFlags_MenuItem | (enabled ? 0 : ImGuiSelectableFlags_Disabled);
+ bool pressed;
+ if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
{
- PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
- RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
- PopStyleColor();
+ // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
+ // Note that in this situation we render neither the shortcut neither the selected tick mark
+ float w = label_size.x;
+ window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
+ PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
+ pressed = Selectable(label, false, flags, ImVec2(w, 0.0f));
+ PopStyleVar();
+ 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().
+ }
+ else
+ {
+ ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
+ float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame
+ float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
+ pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f));
+ if (shortcut_size.x > 0.0f)
+ {
+ PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
+ RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
+ PopStyleColor();
+ }
+ if (selected)
+ RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * (0.20f+0.200f), g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f);
}
-
- if (selected)
- RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled));
-
return pressed;
}
@@ -8863,7 +9194,7 @@ bool ImGui::BeginMenuBar()
window->DC.CursorPos = ImVec2(rect.Min.x + window->DC.MenuBarOffsetX, rect.Min.y);// + g.Style.FramePadding.y);
window->DC.LayoutType = ImGuiLayoutType_Horizontal;
window->DC.MenuBarAppending = true;
- AlignFirstTextHeightToWidgets();
+ AlignTextToFramePadding();
return true;
}
@@ -8907,32 +9238,34 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
ImVec2 popup_pos, pos = window->DC.CursorPos;
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
{
+ // Menu inside an horizontal menu bar
+ // Selectable extend their highlight by half ItemSpacing in each direction.
popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight());
window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f);
float w = label_size.x;
pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
PopStyleVar();
- SameLine();
- window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f);
+ 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().
}
else
{
+ // Menu inside a menu
popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame
float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
- RenderCollapseTriangle(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), false);
+ RenderTriangle(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), ImGuiDir_Right);
if (!enabled) PopStyleColor();
}
- bool hovered = enabled && IsHovered(window->DC.LastItemRect, id);
+ const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id);
if (menuset_is_open)
g.NavWindow = backed_nav_window;
bool want_open = false, want_close = false;
- if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
+ if (window->DC.LayoutType != ImGuiLayoutType_Horizontal) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
{
// Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
bool moving_within_opened_triangle = false;
@@ -8956,13 +9289,20 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle);
want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed);
}
- else if (menu_is_open && pressed && menuset_is_open) // Menu bar: click an open menu again to close it
+ else
{
- want_close = true;
- want_open = menu_is_open = false;
+ // Menu bar
+ if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it
+ {
+ want_close = true;
+ want_open = menu_is_open = false;
+ }
+ else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others
+ {
+ want_open = true;
+ }
}
- else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // menu-bar: first click to open, then hover to open others
- want_open = true;
+
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.. }'
want_close = true;
if (want_close && IsPopupOpen(id))
@@ -8982,7 +9322,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
if (menu_is_open)
{
SetNextWindowPos(popup_pos, ImGuiCond_Always);
- ImGuiWindowFlags flags = ImGuiWindowFlags_ShowBorders | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu);
+ ImGuiWindowFlags flags = ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_AlwaysAutoResize | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu);
menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
}
@@ -8995,12 +9335,12 @@ void ImGui::EndMenu()
}
// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
-void ImGui::ColorTooltip(const char* text, const float col[4], ImGuiColorEditFlags flags)
+void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags)
{
ImGuiContext& g = *GImGui;
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]);
- BeginTooltipEx(true);
+ BeginTooltipEx(0, true);
const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text;
if (text_end > text)
@@ -9019,12 +9359,6 @@ void ImGui::ColorTooltip(const char* text, const float col[4], ImGuiColorEditFla
EndTooltip();
}
-static inline float ColorSquareSize()
-{
- ImGuiContext& g = *GImGui;
- return g.FontSize + g.Style.FramePadding.y * 2.0f;
-}
-
static inline ImU32 ImAlphaBlendColor(ImU32 col_a, ImU32 col_b)
{
float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
@@ -9058,7 +9392,7 @@ void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU
continue;
int rounding_corners_flags_cell = 0;
if (y1 <= p_min.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImGuiCorner_TopLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImGuiCorner_TopRight; }
- if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImGuiCorner_BottomLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImGuiCorner_BottomRight; }
+ if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImGuiCorner_BotLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImGuiCorner_BotRight; }
rounding_corners_flags_cell &= rounding_corners_flags;
window->DrawList->AddRectFilled(ImVec2(x1,y1), ImVec2(x2,y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell);
}
@@ -9096,14 +9430,14 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
ImGuiContext& g = *GImGui;
const ImGuiID id = window->GetID(desc_id);
- float default_size = ColorSquareSize();
+ float default_size = SmallSquareSize();
if (size.x == 0.0f)
size.x = default_size;
if (size.y == 0.0f)
size.y = default_size;
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
- ItemSize(bb);
- if (!ItemAdd(bb, &id))
+ ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f);
+ if (!ItemAdd(bb, id))
return false;
bool hovered, held;
@@ -9115,15 +9449,23 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
ImVec4 col_without_alpha(col.x, col.y, col.z, 1.0f);
float grid_step = ImMin(size.x, size.y) / 2.99f;
float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f);
+ ImRect bb_inner = bb;
+ 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.
+ bb_inner.Expand(off);
if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f)
{
- float mid_x = (float)(int)((bb.Min.x + bb.Max.x) * 0.5f + 0.5f);
- RenderColorRectWithAlphaCheckerboard(ImVec2(bb.Min.x + grid_step, bb.Min.y), bb.Max, GetColorU32(col), grid_step, ImVec2(-grid_step, 0.0f), rounding, ImGuiCorner_TopRight|ImGuiCorner_BottomRight);
- window->DrawList->AddRectFilled(bb.Min, ImVec2(mid_x, bb.Max.y), GetColorU32(col_without_alpha), rounding, ImGuiCorner_TopLeft|ImGuiCorner_BottomLeft);
+ float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f);
+ 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, ImGuiCorner_TopRight|ImGuiCorner_BotRight);
+ window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImGuiCorner_TopLeft|ImGuiCorner_BotLeft);
}
else
{
- RenderColorRectWithAlphaCheckerboard(bb.Min, bb.Max, GetColorU32((flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha), grid_step, ImVec2(0,0), rounding);
+ // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha
+ ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha;
+ if (col_source.w < 1.0f)
+ RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding);
+ else
+ window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ~0);
}
if (window->Flags & ImGuiWindowFlags_ShowBorders)
RenderFrameBorder(bb.Min, bb.Max, rounding);
@@ -9141,28 +9483,52 @@ bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flag
return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha);
}
-static void ColorEditOptionsPopup(ImGuiColorEditFlags flags)
+void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
{
bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__InputsMask);
bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask);
- if ((!allow_opt_inputs && !allow_opt_datatype) || !ImGui::BeginPopup("context"))
+ if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context"))
return;
ImGuiContext& g = *GImGui;
ImGuiColorEditFlags opts = g.ColorEditOptions;
if (allow_opt_inputs)
{
- if (ImGui::RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB;
- if (ImGui::RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV;
- if (ImGui::RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX;
+ if (RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB;
+ if (RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV;
+ if (RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX;
}
if (allow_opt_datatype)
{
- if (allow_opt_inputs) ImGui::Separator();
- if (ImGui::RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8;
- if (ImGui::RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float;
+ if (allow_opt_inputs) Separator();
+ if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8;
+ if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float;
+ }
+
+ if (allow_opt_inputs || allow_opt_datatype)
+ Separator();
+ if (Button("Copy as..", ImVec2(-1,0)))
+ OpenPopup("Copy");
+ if (BeginPopup("Copy"))
+ {
+ 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]);
+ char buf[64];
+ ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
+ if (Selectable(buf))
+ SetClipboardText(buf);
+ ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca);
+ if (Selectable(buf))
+ SetClipboardText(buf);
+ if (flags & ImGuiColorEditFlags_NoAlpha)
+ ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X", cr, cg, cb);
+ else
+ ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X%02X", cr, cg, cb, ca);
+ if (Selectable(buf))
+ SetClipboardText(buf);
+ EndPopup();
}
+
g.ColorEditOptions = opts;
- ImGui::EndPopup();
+ EndPopup();
}
static void ColorPickerOptionsPopup(ImGuiColorEditFlags flags, float* ref_col)
@@ -9174,7 +9540,7 @@ static void ColorPickerOptionsPopup(ImGuiColorEditFlags flags, float* ref_col)
ImGuiContext& g = *GImGui;
if (allow_opt_picker)
{
- ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (ColorSquareSize() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function
+ ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (SmallSquareSize() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function
ImGui::PushItemWidth(picker_size.x);
for (int picker_type = 0; picker_type < 2; picker_type++)
{
@@ -9214,7 +9580,8 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
- const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (ColorSquareSize() + style.ItemInnerSpacing.x);
+ const float square_sz = SmallSquareSize();
+ const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x);
const float w_items_all = CalcItemWidth() - w_extra;
const char* label_display_end = FindRenderedTextEnd(label);
@@ -9232,7 +9599,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
// Context menu: display and modify options (before defaults are applied)
if (!(flags & ImGuiColorEditFlags_NoOptions))
- ColorEditOptionsPopup(flags);
+ ColorEditOptionsPopup(col, flags);
// Read stored options
if (!(flags & ImGuiColorEditFlags__InputsMask))
@@ -9285,8 +9652,8 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
value_changed |= value_changed_as_float |= DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]);
else
value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]);
- if (!(flags & ImGuiColorEditFlags_NoOptions) && IsItemHovered() && IsMouseClicked(1))
- OpenPopup("context");
+ if (!(flags & ImGuiColorEditFlags_NoOptions))
+ OpenPopupOnItemClick("context");
}
PopItemWidth();
PopItemWidth();
@@ -9312,8 +9679,8 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
else
sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
}
- if (!(flags & ImGuiColorEditFlags_NoOptions) && IsItemHovered() && IsMouseClicked(1))
- OpenPopup("context");
+ if (!(flags & ImGuiColorEditFlags_NoOptions))
+ OpenPopupOnItemClick("context");
PopItemWidth();
}
@@ -9334,9 +9701,9 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1,style.ItemSpacing.y));
}
}
- if (!(flags & ImGuiColorEditFlags_NoOptions) && IsItemHovered() && IsMouseClicked(1))
- OpenPopup("context");
-
+ if (!(flags & ImGuiColorEditFlags_NoOptions))
+ OpenPopupOnItemClick("context");
+
if (BeginPopup("picker"))
{
picker_active = true;
@@ -9345,7 +9712,6 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
TextUnformatted(label, label_display_end);
Separator();
}
- float square_sz = ColorSquareSize();
ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar;
ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf;
PushItemWidth(square_sz * 12.0f); // Use 256 + bar sizes?
@@ -9415,21 +9781,6 @@ static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2
RenderArrow(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32_WHITE);
}
-static void PaintVertsLinearGradientKeepAlpha(ImDrawVert* vert_start, ImDrawVert* vert_end, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1)
-{
- ImVec2 gradient_extent = gradient_p1 - gradient_p0;
- float gradient_inv_length = ImInvLength(gradient_extent, 0.0f);
- for (ImDrawVert* vert = vert_start; vert < vert_end; vert++)
- {
- float d = ImDot(vert->pos - gradient_p0, gradient_extent);
- float t = ImMin(sqrtf(ImMax(d, 0.0f)) * gradient_inv_length, 1.0f);
- int r = ImLerp((int)(col0 >> IM_COL32_R_SHIFT) & 0xFF, (int)(col1 >> IM_COL32_R_SHIFT) & 0xFF, t);
- int g = ImLerp((int)(col0 >> IM_COL32_G_SHIFT) & 0xFF, (int)(col1 >> IM_COL32_G_SHIFT) & 0xFF, t);
- int b = ImLerp((int)(col0 >> IM_COL32_B_SHIFT) & 0xFF, (int)(col1 >> IM_COL32_B_SHIFT) & 0xFF, t);
- vert->col = (r << IM_COL32_R_SHIFT) | (g << IM_COL32_G_SHIFT) | (b << IM_COL32_B_SHIFT) | (vert->col & IM_COL32_A_MASK);
- }
-}
-
// ColorPicker
// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
// 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..)
@@ -9462,7 +9813,8 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
// Setup
bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha);
ImVec2 picker_pos = window->DC.CursorPos;
- float bars_width = ColorSquareSize(); // Arbitrary smallish width of Hue/Alpha picking bars
+ float square_sz = SmallSquareSize();
+ float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars
float sv_picker_size = ImMax(bars_width * 1, CalcItemWidth() - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box
float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x;
float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x;
@@ -9516,8 +9868,8 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
value_changed = value_changed_sv = true;
}
}
- if (!(flags & ImGuiColorEditFlags_NoOptions) && IsItemHovered() && IsMouseClicked(1))
- OpenPopup("context");
+ if (!(flags & ImGuiColorEditFlags_NoOptions))
+ OpenPopupOnItemClick("context");
}
else if (flags & ImGuiColorEditFlags_PickerHueBar)
{
@@ -9529,8 +9881,8 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1));
value_changed = value_changed_sv = true;
}
- if (!(flags & ImGuiColorEditFlags_NoOptions) && IsItemHovered() && IsMouseClicked(1))
- OpenPopup("context");
+ if (!(flags & ImGuiColorEditFlags_NoOptions))
+ OpenPopupOnItemClick("context");
// Hue bar logic
SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));
@@ -9574,7 +9926,6 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
if (!(flags & ImGuiColorEditFlags_NoSidePreview))
{
ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
- float square_sz = ColorSquareSize();
if ((flags & ImGuiColorEditFlags_NoLabel))
Text("Current");
ColorButton("##current", col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2));
@@ -9647,7 +9998,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
// Paint colors over existing vertices
ImVec2 gradient_p0(wheel_center.x + cosf(a0) * wheel_r_inner, wheel_center.y + sinf(a0) * wheel_r_inner);
ImVec2 gradient_p1(wheel_center.x + cosf(a1) * wheel_r_inner, wheel_center.y + sinf(a1) * wheel_r_inner);
- PaintVertsLinearGradientKeepAlpha(draw_list->_VtxWritePtr - (draw_list->_VtxCurrentIdx - vert_start_idx), draw_list->_VtxWritePtr, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n+1]);
+ ShadeVertsLinearColorGradientKeepAlpha(draw_list->_VtxWritePtr - (draw_list->_VtxCurrentIdx - vert_start_idx), draw_list->_VtxWritePtr, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n+1]);
}
// Render Cursor + preview on Hue Wheel
@@ -9722,7 +10073,19 @@ void ImGui::Separator()
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
+ ImGuiContext& g = *GImGui;
+
+ ImGuiWindowFlags flags = 0;
+ if ((flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)) == 0)
+ flags |= (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal;
+ IM_ASSERT(ImIsPowerOfTwo((int)(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)))); // Check that only 1 option is selected
+ if (flags & ImGuiSeparatorFlags_Vertical)
+ {
+ VerticalSeparator();
+ return;
+ }
+ // Horizontal Separator
if (window->DC.ColumnsCount > 1)
PopClipRect();
@@ -9733,7 +10096,7 @@ void ImGui::Separator()
const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f));
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.
- if (!ItemAdd(bb, NULL))
+ if (!ItemAdd(bb, 0))
{
if (window->DC.ColumnsCount > 1)
PushColumnClipRect();
@@ -9742,9 +10105,8 @@ void ImGui::Separator()
window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Separator));
- ImGuiContext& g = *GImGui;
if (g.LogEnabled)
- LogText(IM_NEWLINE "--------------------------------");
+ LogRenderedText(NULL, IM_NEWLINE "--------------------------------");
if (window->DC.ColumnsCount > 1)
{
@@ -9753,6 +10115,25 @@ void ImGui::Separator()
}
}
+void ImGui::VerticalSeparator()
+{
+ ImGuiWindow* window = GetCurrentWindow();
+ if (window->SkipItems)
+ return;
+ ImGuiContext& g = *GImGui;
+
+ float y1 = window->DC.CursorPos.y;
+ float y2 = window->DC.CursorPos.y + window->DC.CurrentLineHeight;
+ const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + 1.0f, y2));
+ ItemSize(ImVec2(bb.GetWidth(), 0.0f));
+ if (!ItemAdd(bb, 0))
+ return;
+
+ window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x, bb.Max.y), GetColorU32(ImGuiCol_Separator));
+ if (g.LogEnabled)
+ LogText(" |");
+}
+
void ImGui::Spacing()
{
ImGuiWindow* window = GetCurrentWindow();
@@ -9769,7 +10150,7 @@ void ImGui::Dummy(const ImVec2& size)
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
ItemSize(bb);
- ItemAdd(bb, NULL);
+ ItemAdd(bb, 0);
}
bool ImGui::IsRectVisible(const ImVec2& size)
@@ -9833,16 +10214,15 @@ void ImGui::EndGroup()
{
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.
ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset);
- ItemAdd(group_bb, NULL);
+ ItemAdd(group_bb, 0);
}
- // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive() will function on the entire group.
+ // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive() will be functional on the entire group.
// It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but if you search for LastItemId you'll notice it is only used in that context.
const bool active_id_within_group = (!group_data.BackupActiveIdIsAlive && g.ActiveIdIsAlive && g.ActiveId && g.ActiveIdWindow->RootWindow == window->RootWindow);
if (active_id_within_group)
window->DC.LastItemId = g.ActiveId;
- if (active_id_within_group && g.HoveredId == g.ActiveId)
- window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = true;
+ window->DC.LastItemRect = group_bb;
window->DC.GroupStack.pop_back();
@@ -9882,10 +10262,15 @@ void ImGui::NewLine()
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return;
+
+ ImGuiContext& g = *GImGui;
+ const ImGuiLayoutType backup_layout_type = window->DC.LayoutType;
+ window->DC.LayoutType = ImGuiLayoutType_Vertical;
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.
ItemSize(ImVec2(0,0));
else
- ItemSize(ImVec2(0.0f, GImGui->FontSize));
+ ItemSize(ImVec2(0.0f, g.FontSize));
+ window->DC.LayoutType = backup_layout_type;
}
void ImGui::NextColumn()
@@ -9948,7 +10333,7 @@ static float GetDraggedColumnOffset(int column_index)
// Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
// window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
ImGuiContext& g = *GImGui;
- ImGuiWindow* window = ImGui::GetCurrentWindowRead();
+ ImGuiWindow* window = g.CurrentWindow;
IM_ASSERT(column_index > 0); // We cannot drag column 0. If you get this assert you may have a conflict between the ID of your columns and another widgets.
IM_ASSERT(g.ActiveId == window->DC.ColumnsSetId + ImGuiID(column_index));
@@ -10115,7 +10500,7 @@ void ImGui::EndColumns()
const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(i);
const float column_w = 4.0f; // Width for interaction
const ImRect column_rect(ImVec2(x - column_w, y1), ImVec2(x + column_w, y2));
- if (IsClippedEx(column_rect, &column_id, false))
+ if (IsClippedEx(column_rect, column_id, false))
continue;
bool hovered = false, held = false;
@@ -10249,14 +10634,14 @@ void ImGui::Value(const char* prefix, float v, const char* float_format)
// PLATFORM DEPENDENT HELPERS
//-----------------------------------------------------------------------------
-#if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS))
+#if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
#undef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
// Win32 API clipboard implementation
-#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS)
+#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
#ifdef _MSC_VER
#pragma comment(lib, "user32")
@@ -10292,7 +10677,10 @@ static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
if (wbuf_handle == NULL)
+ {
+ CloseClipboard();
return;
+ }
ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle);
ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
GlobalUnlock(wbuf_handle);
@@ -10316,7 +10704,7 @@ static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
ImGuiContext& g = *GImGui;
g.PrivateClipboard.clear();
const char* text_end = text + strlen(text);
- g.PrivateClipboard.resize((size_t)(text_end - text) + 1);
+ g.PrivateClipboard.resize((int)(text_end - text) + 1);
memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
g.PrivateClipboard[(int)(text_end - text)] = 0;
}
@@ -10324,7 +10712,7 @@ static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
#endif
// Win32 API IME support (for Asian languages, etc.)
-#if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS)
+#if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
#include <imm.h>
#ifdef _MSC_VER
@@ -10387,6 +10775,8 @@ void ImGui::ShowMetricsWindow(bool* p_open)
int elem_offset = 0;
for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
{
+ if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
+ continue;
if (pcmd->UserCallback)
{
ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
@@ -10409,13 +10799,14 @@ void ImGui::ShowMetricsWindow(bool* p_open)
while (clipper.Step())
for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
{
- char buf[300], *buf_p = buf;
+ char buf[300];
+ char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
ImVec2 triangles_pos[3];
for (int n = 0; n < 3; n++, vtx_i++)
{
ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i];
triangles_pos[n] = v.pos;
- buf_p += sprintf(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);
+ 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);
}
ImGui::Selectable(buf, false);
if (ImGui::IsItemHovered())
@@ -10441,8 +10832,9 @@ void ImGui::ShowMetricsWindow(bool* p_open)
if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
return;
NodeDrawList(window->DrawList, "DrawList");
- ImGui::BulletText("Pos: (%.1f,%.1f)", window->Pos.x, window->Pos.y);
- ImGui::BulletText("Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y);
+ 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);
+ if (ImGui::IsItemHovered())
+ GImGui->OverlayDrawList.AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255,255,0,255));
ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y);
ImGui::BulletText("Active: %d, Accessed: %d", window->Active, window->Accessed);
if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
@@ -10456,8 +10848,9 @@ void ImGui::ShowMetricsWindow(bool* p_open)
Funcs::NodeWindows(g.Windows, "Windows");
if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.RenderDrawLists[0].Size))
{
- for (int i = 0; i < g.RenderDrawLists[0].Size; i++)
- Funcs::NodeDrawList(g.RenderDrawLists[0][i], "DrawList");
+ for (int layer = 0; layer < IM_ARRAYSIZE(g.RenderDrawLists); layer++)
+ for (int i = 0; i < g.RenderDrawLists[layer].Size; i++)
+ Funcs::NodeDrawList(g.RenderDrawLists[0][i], "DrawList");
ImGui::TreePop();
}
if (ImGui::TreeNode("Popups", "Open Popups Stack (%d)", g.OpenPopupStack.Size))