Updated imgui.

This commit is contained in:
Branimir Karadžić 2015-08-25 20:44:36 -07:00
parent 2c3bd9ce95
commit 06624bb7c2
7 changed files with 566 additions and 280 deletions

View file

@ -128,8 +128,10 @@
// swap video buffer, etc. // swap video buffer, etc.
} }
- after calling ImGui::NewFrame() you can read back 'io.WantCaptureMouse' and 'io.WantCaptureKeyboard' to tell if ImGui - after calling ImGui::NewFrame() you can read back flags from the IO structure to tell how ImGui intends to use your inputs.
wants to use your inputs. if it does you can discard/hide the inputs from the rest of your application. 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.WantInputsCharacters' is set to may want to notify your OS to popup an onscreen keyboard, if available.
API BREAKING CHANGES API BREAKING CHANGES
==================== ====================
@ -1901,6 +1903,7 @@ void ImGui::NewFrame()
bool mouse_owned_by_application = mouse_earliest_button_down != -1 && !g.IO.MouseDownOwned[mouse_earliest_button_down]; bool mouse_owned_by_application = mouse_earliest_button_down != -1 && !g.IO.MouseDownOwned[mouse_earliest_button_down];
g.IO.WantCaptureMouse = (!mouse_owned_by_application && g.HoveredWindow != NULL) || (!mouse_owned_by_application && mouse_any_down) || (g.ActiveId != 0) || (!g.OpenedPopupStack.empty()) || (g.CaptureMouseNextFrame); g.IO.WantCaptureMouse = (!mouse_owned_by_application && g.HoveredWindow != NULL) || (!mouse_owned_by_application && mouse_any_down) || (g.ActiveId != 0) || (!g.OpenedPopupStack.empty()) || (g.CaptureMouseNextFrame);
g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (g.CaptureKeyboardNextFrame); g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (g.CaptureKeyboardNextFrame);
g.IO.WantInputCharacters = (g.ActiveId != 0 && g.InputTextState.Id == g.ActiveId);
g.MouseCursor = ImGuiMouseCursor_Arrow; g.MouseCursor = ImGuiMouseCursor_Arrow;
g.CaptureMouseNextFrame = g.CaptureKeyboardNextFrame = false; g.CaptureMouseNextFrame = g.CaptureKeyboardNextFrame = false;
@ -2256,10 +2259,14 @@ void ImGui::Render()
if (!(g.FocusedWindow && !g.FocusedWindow->WasActive && g.FocusedWindow->Active)) // Unless we just made a popup appear if (!(g.FocusedWindow && !g.FocusedWindow->WasActive && g.FocusedWindow->Active)) // Unless we just made a popup appear
{ {
if (g.HoveredRootWindow != NULL) if (g.HoveredRootWindow != NULL)
{
FocusWindow(g.HoveredWindow);
if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove))
{ {
g.MovedWindow = g.HoveredWindow; g.MovedWindow = g.HoveredWindow;
SetActiveID(g.HoveredRootWindow->MoveID, g.HoveredRootWindow); SetActiveID(g.HoveredRootWindow->MoveID, g.HoveredRootWindow);
} }
}
else if (g.FocusedWindow != NULL && GetFrontMostModalRootWindow() == NULL) else if (g.FocusedWindow != NULL && GetFrontMostModalRootWindow() == NULL)
{ {
// Clicking on void disable focus // Clicking on void disable focus
@ -2826,6 +2833,7 @@ ImVec2 ImGui::GetMousePos()
return GImGui->IO.MousePos; return GImGui->IO.MousePos;
} }
// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
{ {
ImGuiState& g = *GImGui; ImGuiState& g = *GImGui;
@ -3173,7 +3181,7 @@ bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
{ {
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindow();
ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow; ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
const ImVec2 content_avail = ImGui::GetContentRegionAvail(); const ImVec2 content_avail = ImGui::GetContentRegionAvail();
ImVec2 size = size_arg; ImVec2 size = size_arg;
@ -3248,7 +3256,7 @@ bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size)
const ImGuiStyle& style = g.Style; const ImGuiStyle& style = g.Style;
ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, style.Colors[ImGuiCol_FrameBg]); ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, style.Colors[ImGuiCol_FrameBg]);
ImGui::PushStyleVar(ImGuiStyleVar_ChildWindowRounding, style.FrameRounding); ImGui::PushStyleVar(ImGuiStyleVar_ChildWindowRounding, style.FrameRounding);
return ImGui::BeginChild(id, size); return ImGui::BeginChild(id, size, false, ImGuiWindowFlags_NoMove);
} }
void ImGui::EndChildFrame() void ImGui::EndChildFrame()
@ -3898,9 +3906,11 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_
// We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame // 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. // 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(); const ImRect title_bar_rect = window->TitleBarRect();
ImVec4 clip_rect(title_bar_rect.Min.x+0.5f+window->WindowPadding.x*0.5f, title_bar_rect.Max.y+window->MenuBarHeight()+0.5f, window->Rect().Max.x+0.5f-window->WindowPadding.x*0.5f, window->Rect().Max.y-1.5f); ImRect clip_rect(title_bar_rect.Min.x+0.5f+window->WindowPadding.x*0.5f, title_bar_rect.Max.y+window->MenuBarHeight()+0.5f, window->Pos.x+window->Size.x+0.5f-window->WindowPadding.x*0.5f, window->Pos.y+window->Size.y+0.5f);
if ((flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_ShowBorders))
clip_rect.Min += ImVec2(1.0f,1.0f);
if (window->ScrollbarY) if (window->ScrollbarY)
clip_rect.z -= style.ScrollbarWidth; clip_rect.Max.x -= style.ScrollbarWidth;
PushClipRect(clip_rect); PushClipRect(clip_rect);
// Clear 'accessed' flag last thing // Clear 'accessed' flag last thing
@ -4343,6 +4353,12 @@ float ImGui::GetWindowWidth()
return window->Size.x; return window->Size.x;
} }
float ImGui::GetWindowHeight()
{
ImGuiWindow* window = GImGui->CurrentWindow;
return window->Size.y;
}
ImVec2 ImGui::GetWindowPos() ImVec2 ImGui::GetWindowPos()
{ {
ImGuiState& g = *GImGui; ImGuiState& g = *GImGui;
@ -7022,8 +7038,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
if (g.IO.InputCharacters[0]) if (g.IO.InputCharacters[0])
{ {
// Process text input (before we check for Return because using some IME will effectively send a Return?) // Process text input (before we check for Return because using some IME will effectively send a Return?)
for (int n = 0; n < IM_ARRAYSIZE(g.IO.InputCharacters) && g.IO.InputCharacters[n]; n++) if (!is_ctrl_down && !is_alt_down)
{ {
for (int n = 0; n < IM_ARRAYSIZE(g.IO.InputCharacters) && g.IO.InputCharacters[n]; n++)
if (unsigned int c = (unsigned int)g.IO.InputCharacters[n]) if (unsigned int c = (unsigned int)g.IO.InputCharacters[n])
{ {
// Insert character if they pass filtering // Insert character if they pass filtering
@ -8555,7 +8572,7 @@ void ImGui::Columns(int columns_count, const char* id, bool border)
ItemSize(ImVec2(0,0)); // Advance to column 0 ItemSize(ImVec2(0,0)); // Advance to column 0
ImGui::PopItemWidth(); ImGui::PopItemWidth();
PopClipRect(); PopClipRect();
window->DrawList->ChannelsMerge(window->DC.ColumnsCount); window->DrawList->ChannelsMerge();
window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y); window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y);
window->DC.CursorPos.y = window->DC.ColumnsCellMaxY; window->DC.CursorPos.y = window->DC.ColumnsCellMaxY;
@ -8844,7 +8861,7 @@ void ImGui::ShowMetricsWindow(bool* opened)
ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3); ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3);
ImGui::Text("%d allocations", ImGui::GetIO().MetricsAllocs); ImGui::Text("%d allocations", ImGui::GetIO().MetricsAllocs);
static bool show_clip_rects = true; static bool show_clip_rects = true;
ImGui::Checkbox("Show clipping rectangles when hovering ImDrawList", &show_clip_rects); ImGui::Checkbox("Show clipping rectangles when hovering a ImDrawCmd", &show_clip_rects);
ImGui::Separator(); ImGui::Separator();
struct Funcs struct Funcs
@ -8923,6 +8940,15 @@ void ImGui::ShowMetricsWindow(bool* opened)
} }
ImGui::TreePop(); ImGui::TreePop();
} }
if (ImGui::TreeNode("Basic state"))
{
ImGui::Text("FocusedWindow: '%s'", g.FocusedWindow ? g.FocusedWindow->Name : "NULL");
ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
ImGui::Text("HoveredID: 0x%08X/0x%08X", g.HoveredId, g.HoveredIdPreviousFrame); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
ImGui::Text("ActiveID: 0x%08X/0x%08X", g.ActiveId, g.ActiveIdPreviousFrame);
ImGui::TreePop();
}
g.DisableHideTextAfterDoubleHash--; g.DisableHideTextAfterDoubleHash--;
} }
ImGui::End(); ImGui::End();

View file

@ -131,6 +131,7 @@ namespace ImGui
IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList api) IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList api)
IMGUI_API ImVec2 GetWindowSize(); // get current window size IMGUI_API ImVec2 GetWindowSize(); // get current window size
IMGUI_API float GetWindowWidth(); IMGUI_API float GetWindowWidth();
IMGUI_API float GetWindowHeight();
IMGUI_API bool IsWindowCollapsed(); IMGUI_API bool IsWindowCollapsed();
IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond = 0); // set next window position. call before Begin() IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond = 0); // set next window position. call before Begin()
@ -177,7 +178,7 @@ namespace ImGui
IMGUI_API void PopButtonRepeat(); IMGUI_API void PopButtonRepeat();
// Cursor / Layout // Cursor / Layout
IMGUI_API void BeginGroup(); // once closing a group it is seen as a single item (so you can use IsItemHovered() on a group, SameLine() between groups, etc. IMGUI_API void BeginGroup(); // lock horizontal starting position. once closing a group it is seen as a single item (so you can use IsItemHovered() on a group, SameLine() between groups, etc.
IMGUI_API void EndGroup(); IMGUI_API void EndGroup();
IMGUI_API void Separator(); // horizontal line IMGUI_API void Separator(); // horizontal line
IMGUI_API void SameLine(float local_pos_x = 0.0f, float spacing_w = -1.0f); // call between widgets or groups to layout them horizontally IMGUI_API void SameLine(float local_pos_x = 0.0f, float spacing_w = -1.0f); // call between widgets or groups to layout them horizontally
@ -399,7 +400,7 @@ namespace ImGui
IMGUI_API bool IsMouseDragging(int button = 0, float lock_threshold = -1.0f); // is mouse dragging. if lock_threshold < -1.0f uses io.MouseDraggingThreshold IMGUI_API bool IsMouseDragging(int button = 0, float lock_threshold = -1.0f); // is mouse dragging. if lock_threshold < -1.0f uses io.MouseDraggingThreshold
IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls
IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve backup of mouse positioning at the time of opening popup we have BeginPopup() into IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve backup of mouse positioning at the time of opening popup we have BeginPopup() into
IMGUI_API ImVec2 GetMouseDragDelta(int button = 0, float lock_threshold = -1.0f); // dragging amount since clicking, also see: GetItemActiveDragDelta(). if lock_threshold < -1.0f uses io.MouseDraggingThreshold IMGUI_API ImVec2 GetMouseDragDelta(int button = 0, float lock_threshold = -1.0f); // dragging amount since clicking. if lock_threshold < -1.0f uses io.MouseDraggingThreshold
IMGUI_API void ResetMouseDragDelta(int button = 0); // IMGUI_API void ResetMouseDragDelta(int button = 0); //
IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired cursor type, reset in ImGui::NewFrame(), this updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired cursor type, reset in ImGui::NewFrame(), this updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you
IMGUI_API void SetMouseCursor(ImGuiMouseCursor type); // set desired cursor type IMGUI_API void SetMouseCursor(ImGuiMouseCursor type); // set desired cursor type
@ -724,6 +725,7 @@ struct ImGuiIO
bool WantCaptureMouse; // Mouse is hovering a window or widget is active (= ImGui will use your mouse input) bool WantCaptureMouse; // Mouse is hovering a window or widget is active (= ImGui will use your mouse input)
bool WantCaptureKeyboard; // Widget is active (= ImGui will use your keyboard input) bool WantCaptureKeyboard; // Widget is active (= ImGui will use your keyboard input)
bool WantInputCharacters; // Some text input widget is active, which will read input characters from the InputCharacters array.
float Framerate; // Framerate estimation, in frame per second. Rolling average estimation based on IO.DeltaTime over 120 frames float Framerate; // Framerate estimation, in frame per second. Rolling average estimation based on IO.DeltaTime over 120 frames
int MetricsAllocs; // Number of active memory allocations int MetricsAllocs; // Number of active memory allocations
int MetricsRenderVertices; // Vertices output during last call to Render() int MetricsRenderVertices; // Vertices output during last call to Render()
@ -1014,6 +1016,8 @@ struct ImDrawCmd
ImTextureID TextureId; // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas. ImTextureID TextureId; // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas.
ImDrawCallback UserCallback; // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally. ImDrawCallback UserCallback; // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally.
void* UserCallbackData; // The draw callback code can access this. void* UserCallbackData; // The draw callback code can access this.
ImDrawCmd() { ElemCount = 0; ClipRect.x = ClipRect.y = -8192.0f; ClipRect.z = ClipRect.w = +8192.0f; TextureId = NULL; UserCallback = NULL; UserCallbackData = NULL; }
}; };
// Vertex index (override with, e.g. '#define ImDrawIdx unsigned int' in ImConfig) // Vertex index (override with, e.g. '#define ImDrawIdx unsigned int' in ImConfig)
@ -1037,6 +1041,7 @@ IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT;
#endif #endif
// Draw channels are used by the Columns API to "split" the render list into different channels while building, so items of each column can be batched together. // Draw channels are used by the Columns API to "split" the render list into different channels while building, so items of each column can be batched together.
// You can also use them to simulate drawing layers and submit primitives in a different order than how they will be rendered.
struct ImDrawChannel struct ImDrawChannel
{ {
ImVector<ImDrawCmd> CmdBuffer; ImVector<ImDrawCmd> CmdBuffer;
@ -1065,8 +1070,9 @@ struct ImDrawList
ImVector<ImVec4> _ClipRectStack; // [Internal] ImVector<ImVec4> _ClipRectStack; // [Internal]
ImVector<ImTextureID> _TextureIdStack; // [Internal] ImVector<ImTextureID> _TextureIdStack; // [Internal]
ImVector<ImVec2> _Path; // [Internal] current path building ImVector<ImVec2> _Path; // [Internal] current path building
int _ChannelCurrent; // [Internal] current channel number (0) int _ChannelsCurrent; // [Internal] current channel number (0)
ImVector<ImDrawChannel> _Channels; // [Internal] draw channels for columns API int _ChannelsCount; // [Internal] number of active channels (1+)
ImVector<ImDrawChannel> _Channels; // [Internal] draw channels for columns API (not resized down so _ChannelsCount may be smaller than _Channels.Size)
ImDrawList() { _OwnerName = NULL; Clear(); } ImDrawList() { _OwnerName = NULL; Clear(); }
~ImDrawList() { ClearFreeMemory(); } ~ImDrawList() { ClearFreeMemory(); }
@ -1101,12 +1107,16 @@ struct ImDrawList
inline void PathFill(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col, true); PathClear(); } inline void PathFill(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col, true); PathClear(); }
inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness, true); PathClear(); } inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness, true); PathClear(); }
// Channels
// - Use to simulate layers. By switching channels to can render out-of-order (e.g. submit foreground primitives before background primitives)
// - Use to minimize draw calls (e.g. if going back-and-forth between multiple non-overlapping clipping rectangles, prefer to append into separate channels then merge at the end)
IMGUI_API void ChannelsSplit(int channels_count);
IMGUI_API void ChannelsMerge();
IMGUI_API void ChannelsSetCurrent(int channel_index);
// Advanced // Advanced
IMGUI_API void AddCallback(ImDrawCallback callback, void* callback_data); // Your rendering function must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles. IMGUI_API void AddCallback(ImDrawCallback callback, void* callback_data); // Your rendering function must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles.
IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible
IMGUI_API void ChannelsSplit(int channel_count);
IMGUI_API void ChannelsMerge(int channel_count);
IMGUI_API void ChannelsSetCurrent(int idx);
// Internal helpers // Internal helpers
// NB: all primitives needs to be reserved via PrimReserve() beforehand! // NB: all primitives needs to be reserved via PrimReserve() beforehand!

View file

@ -1343,6 +1343,7 @@ void ImGui::ShowTestWindow(bool* opened)
ImGui::Text("WantCaptureMouse: %s", io.WantCaptureMouse ? "true" : "false"); ImGui::Text("WantCaptureMouse: %s", io.WantCaptureMouse ? "true" : "false");
ImGui::Text("WantCaptureKeyboard: %s", io.WantCaptureKeyboard ? "true" : "false"); ImGui::Text("WantCaptureKeyboard: %s", io.WantCaptureKeyboard ? "true" : "false");
ImGui::Text("WantInputCharacters: %s", io.WantInputCharacters ? "true" : "false");
ImGui::Button("Hover me\nto enforce\ninputs capture"); ImGui::Button("Hover me\nto enforce\ninputs capture");
if (ImGui::IsItemHovered()) if (ImGui::IsItemHovered())

View file

@ -85,7 +85,7 @@ using namespace IMGUI_STB_NAMESPACE;
// ImDrawList // ImDrawList
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static ImVec4 GNullClipRect(-9999.0f,-9999.0f, +9999.0f, +9999.0f); static ImVec4 GNullClipRect(-8192.0f, -8192.0f, +8192.0f, +8192.0f); // Large values that are easy to encode in a few bits+shift
void ImDrawList::Clear() void ImDrawList::Clear()
{ {
@ -98,7 +98,8 @@ void ImDrawList::Clear()
_ClipRectStack.resize(0); _ClipRectStack.resize(0);
_TextureIdStack.resize(0); _TextureIdStack.resize(0);
_Path.resize(0); _Path.resize(0);
_ChannelCurrent = 0; _ChannelsCurrent = 0;
_ChannelsCount = 1;
// NB: Do not clear channels so our allocations are re-used after the first frame. // NB: Do not clear channels so our allocations are re-used after the first frame.
} }
@ -113,7 +114,8 @@ void ImDrawList::ClearFreeMemory()
_ClipRectStack.clear(); _ClipRectStack.clear();
_TextureIdStack.clear(); _TextureIdStack.clear();
_Path.clear(); _Path.clear();
_ChannelCurrent = 0; _ChannelsCurrent = 0;
_ChannelsCount = 1;
for (int i = 0; i < _Channels.Size; i++) for (int i = 0; i < _Channels.Size; i++)
{ {
if (i == 0) memset(&_Channels[0], 0, sizeof(_Channels[0])); // channel 0 is a copy of CmdBuffer/IdxBuffer, don't destruct again if (i == 0) memset(&_Channels[0], 0, sizeof(_Channels[0])); // channel 0 is a copy of CmdBuffer/IdxBuffer, don't destruct again
@ -126,11 +128,8 @@ void ImDrawList::ClearFreeMemory()
void ImDrawList::AddDrawCmd() void ImDrawList::AddDrawCmd()
{ {
ImDrawCmd draw_cmd; ImDrawCmd draw_cmd;
draw_cmd.ElemCount = 0;
draw_cmd.ClipRect = _ClipRectStack.Size ? _ClipRectStack.back() : GNullClipRect; draw_cmd.ClipRect = _ClipRectStack.Size ? _ClipRectStack.back() : GNullClipRect;
draw_cmd.TextureId = _TextureIdStack.Size ? _TextureIdStack.back() : NULL; draw_cmd.TextureId = _TextureIdStack.Size ? _TextureIdStack.back() : NULL;
draw_cmd.UserCallback = NULL;
draw_cmd.UserCallbackData = NULL;
IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w); IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w);
CmdBuffer.push_back(draw_cmd); CmdBuffer.push_back(draw_cmd);
@ -151,24 +150,34 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
AddDrawCmd(); AddDrawCmd();
} }
void ImDrawList::ChannelsSplit(int channel_count) void ImDrawList::ChannelsSplit(int channels_count)
{ {
IM_ASSERT(_ChannelCurrent == 0); IM_ASSERT(_ChannelsCurrent == 0 && _ChannelsCount == 1);
int old_channels_count = _Channels.Size; int old_channels_count = _Channels.Size;
if (old_channels_count < channel_count) if (old_channels_count < channels_count)
_Channels.resize(channel_count); _Channels.resize(channels_count);
for (int i = 0; i < channel_count; i++) _ChannelsCount = channels_count;
for (int i = 0; i < channels_count; i++)
{
if (i >= old_channels_count) if (i >= old_channels_count)
new(&_Channels[i]) ImDrawChannel(); new(&_Channels[i]) ImDrawChannel();
else else if (i > 0)
_Channels[i].CmdBuffer.resize(0), _Channels[i].IdxBuffer.resize(0); _Channels[i].CmdBuffer.resize(0), _Channels[i].IdxBuffer.resize(0);
if (_Channels[i].CmdBuffer.Size == 0)
{
ImDrawCmd draw_cmd;
draw_cmd.ClipRect = _ClipRectStack.back();
draw_cmd.TextureId = _TextureIdStack.back();
_Channels[i].CmdBuffer.push_back(draw_cmd);
}
}
} }
void ImDrawList::ChannelsMerge(int channel_count) void ImDrawList::ChannelsMerge()
{ {
// Note that we never use or rely on channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use. // Note that we never use or rely on channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use.
// This is why this function takes 'channel_count' as a parameter of how many channels to merge (the user knows) // This is why this function takes 'channel_count' as a parameter of how many channels to merge (the user knows)
if (channel_count < 2) if (_ChannelsCount <= 1)
return; return;
ChannelsSetCurrent(0); ChannelsSetCurrent(0);
@ -176,7 +185,7 @@ void ImDrawList::ChannelsMerge(int channel_count)
CmdBuffer.pop_back(); CmdBuffer.pop_back();
int new_cmd_buffer_count = 0, new_idx_buffer_count = 0; int new_cmd_buffer_count = 0, new_idx_buffer_count = 0;
for (int i = 1; i < channel_count; i++) for (int i = 1; i < _ChannelsCount; i++)
{ {
ImDrawChannel& ch = _Channels[i]; ImDrawChannel& ch = _Channels[i];
if (ch.CmdBuffer.Size && ch.CmdBuffer.back().ElemCount == 0) if (ch.CmdBuffer.Size && ch.CmdBuffer.back().ElemCount == 0)
@ -189,23 +198,24 @@ void ImDrawList::ChannelsMerge(int channel_count)
ImDrawCmd* cmd_write = CmdBuffer.Data + CmdBuffer.Size - new_cmd_buffer_count; ImDrawCmd* cmd_write = CmdBuffer.Data + CmdBuffer.Size - new_cmd_buffer_count;
_IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size - new_idx_buffer_count; _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size - new_idx_buffer_count;
for (int i = 1; i < channel_count; i++) for (int i = 1; i < _ChannelsCount; i++)
{ {
ImDrawChannel& ch = _Channels[i]; ImDrawChannel& ch = _Channels[i];
if (int sz = ch.CmdBuffer.Size) { memcpy(cmd_write, ch.CmdBuffer.Data, sz * sizeof(ImDrawCmd)); cmd_write += sz; } if (int sz = ch.CmdBuffer.Size) { memcpy(cmd_write, ch.CmdBuffer.Data, sz * sizeof(ImDrawCmd)); cmd_write += sz; }
if (int sz = ch.IdxBuffer.Size) { memcpy(_IdxWritePtr, ch.IdxBuffer.Data, sz * sizeof(ImDrawIdx)); _IdxWritePtr += sz; } if (int sz = ch.IdxBuffer.Size) { memcpy(_IdxWritePtr, ch.IdxBuffer.Data, sz * sizeof(ImDrawIdx)); _IdxWritePtr += sz; }
} }
AddDrawCmd(); AddDrawCmd();
_ChannelsCount = 1;
} }
void ImDrawList::ChannelsSetCurrent(int idx) void ImDrawList::ChannelsSetCurrent(int idx)
{ {
if (_ChannelCurrent == idx) return; if (_ChannelsCurrent == idx) return;
memcpy(&_Channels.Data[_ChannelCurrent].CmdBuffer, &CmdBuffer, sizeof(CmdBuffer)); memcpy(&_Channels.Data[_ChannelsCurrent].CmdBuffer, &CmdBuffer, sizeof(CmdBuffer)); // copy 12 bytes, four times
memcpy(&_Channels.Data[_ChannelCurrent].IdxBuffer, &IdxBuffer, sizeof(IdxBuffer)); memcpy(&_Channels.Data[_ChannelsCurrent].IdxBuffer, &IdxBuffer, sizeof(IdxBuffer));
_ChannelCurrent = idx; _ChannelsCurrent = idx;
memcpy(&CmdBuffer, &_Channels.Data[_ChannelCurrent].CmdBuffer, sizeof(CmdBuffer)); memcpy(&CmdBuffer, &_Channels.Data[_ChannelsCurrent].CmdBuffer, sizeof(CmdBuffer));
memcpy(&IdxBuffer, &_Channels.Data[_ChannelCurrent].IdxBuffer, sizeof(IdxBuffer)); memcpy(&IdxBuffer, &_Channels.Data[_ChannelsCurrent].IdxBuffer, sizeof(IdxBuffer));
_IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size; _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size;
} }

View file

@ -0,0 +1,231 @@
// Creating a node graph editor for ImGui
// Quick demo, not production code!
// See https://github.com/ocornut/imgui/issues/306
// v0.01
// Animated gif: https://cloud.githubusercontent.com/assets/8225057/9472357/c0263c04-4b4c-11e5-9fdf-2cd4f33f6582.gif
#include <math.h>
#include "imgui.h"
// NB: You can use math functions/operators on ImVec2 if you #define IMGUI_DEFINE_MATH_OPERATORS and #include "imgui_internal.h"
// Here we only declare simple +/- operators so others don't leak into the demo code.
static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x+rhs.x, lhs.y+rhs.y); }
static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x-rhs.x, lhs.y-rhs.y); }
// Really dumb data structure provided for the example.
// Note that we storing links are INDICES (not ID) to make example code shorter, obviously a bad idea for any general purpose code.
void ShowExampleAppCustomNodeGraph(bool* opened)
{
ImGui::SetNextWindowSize(ImVec2(700,600), ImGuiSetCond_FirstUseEver);
if (!ImGui::Begin("Example: Custom Node Graph", opened))
{
ImGui::End();
return;
}
struct Node
{
int ID;
char Name[32];
ImVec2 Pos, Size;
float Value;
int InputsCount, OutputsCount;
Node(int id, const char* name, const ImVec2& pos, float value, int inputs_count, int outputs_count) { ID = id; strncpy(Name, name, 31); Name[31] = 0; Pos = pos; Value = value; InputsCount = inputs_count; OutputsCount = outputs_count; }
ImVec2 GetInputSlotPos(int slot_no) const { return ImVec2(Pos.x, Pos.y + Size.y * ((float)slot_no+1) / ((float)InputsCount+1)); }
ImVec2 GetOutputSlotPos(int slot_no) const { return ImVec2(Pos.x + Size.x, Pos.y + Size.y * ((float)slot_no+1) / ((float)OutputsCount+1)); }
};
struct NodeLink
{
int InputIdx, InputSlot, OutputIdx, OutputSlot;
NodeLink(int input_idx, int input_slot, int output_idx, int output_slot) { InputIdx = input_idx; InputSlot = input_slot; OutputIdx = output_idx; OutputSlot = output_slot; }
};
static ImVector<Node> nodes;
static ImVector<NodeLink> links;
static bool inited = false;
static ImVec2 scrolling = ImVec2(0.0f, 0.0f);
static int node_selected = -1;
if (!inited)
{
nodes.push_back(Node(0, "MainTex", ImVec2(40,50), 0.5f, 1, 1));
nodes.push_back(Node(1, "BumpMap", ImVec2(40,150), 0.42f, 1, 1));
nodes.push_back(Node(2, "Combine", ImVec2(270,80), 1.0f, 2, 2));
links.push_back(NodeLink(0, 0, 2, 0));
links.push_back(NodeLink(1, 0, 2, 1));
inited = true;
}
// Draw a list of nodes on the left side
bool open_context_menu = false;
int node_hovered_in_list = -1;
int node_hovered_in_scene = -1;
ImGui::BeginChild("node_list", ImVec2(100,0));
ImGui::Text("Nodes");
ImGui::Separator();
for (int node_idx = 0; node_idx < nodes.Size; node_idx++)
{
Node* node = &nodes[node_idx];
ImGui::PushID(node->ID);
if (ImGui::Selectable(node->Name, node->ID == node_selected))
node_selected = node->ID;
if (ImGui::IsItemHovered())
{
node_hovered_in_list = node->ID;
open_context_menu |= ImGui::IsMouseClicked(1);
}
ImGui::PopID();
}
ImGui::EndChild();
ImGui::SameLine();
ImGui::BeginGroup();
const float NODE_SLOT_RADIUS = 4.0f;
const ImVec2 NODE_WINDOW_PADDING(8.0f, 8.0f);
// Create our child canvas
ImGui::Text("Hold middle mouse button to scroll (%.2f,%.2f)", scrolling.x, scrolling.y);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(1,1));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0,0));
ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, ImColor(40,40,40,200));
ImGui::BeginChild("scrolling_region", ImVec2(0,0), true, ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoMove);
ImGui::PushItemWidth(120.0f);
ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->ChannelsSplit(2);
ImVec2 offset = ImGui::GetCursorScreenPos() - scrolling;
// Display links
draw_list->ChannelsSetCurrent(0); // Background
for (int link_idx = 0; link_idx < links.Size; link_idx++)
{
NodeLink* link = &links[link_idx];
Node* node_inp = &nodes[link->InputIdx];
Node* node_out = &nodes[link->OutputIdx];
#if 1
// Hermite spline
// TODO: move to ImDrawList path API
ImVec2 p1 = offset+node_inp->GetOutputSlotPos(link->InputSlot);
ImVec2 t1 = ImVec2(+80.0f, 0.0f);
ImVec2 p2 = offset+node_out->GetInputSlotPos(link->OutputSlot);
ImVec2 t2 = ImVec2(+80.0f, 0.0f);
const int STEPS = 12;
for (int step = 0; step <= STEPS; step++)
{
float t = (float)step / (float)STEPS;
float h1 = +2*t*t*t - 3*t*t + 1.0f;
float h2 = -2*t*t*t + 3*t*t;
float h3 = t*t*t - 2*t*t + t;
float h4 = t*t*t - t*t;
draw_list->PathLineTo(ImVec2(h1*p1.x + h2*p2.x + h3*t1.x + h4*t2.x, h1*p1.y + h2*p2.y + h3*t1.y + h4*t2.y));
}
draw_list->PathStroke(ImColor(200,200,100), false, 3.0f);
#else
draw_list->AddLine(offset+node_inp->GetOutputSlotPos(link->InputSlot), offset+node_out->GetInputSlotPos(link->OutputSlot), ImColor(200,200,100), 3.0f);
#endif
}
// Display nodes
for (int node_idx = 0; node_idx < nodes.Size; node_idx++)
{
Node* node = &nodes[node_idx];
ImGui::PushID(node->ID);
ImVec2 node_rect_min = offset + node->Pos;
// Display node contents first
draw_list->ChannelsSetCurrent(1); // Foreground
bool old_any_active = ImGui::IsAnyItemActive();
ImGui::SetCursorScreenPos(node_rect_min + NODE_WINDOW_PADDING);
ImGui::BeginGroup(); // Lock horizontal position
ImGui::Text("%s", node->Name);
ImGui::SliderFloat("##value", &node->Value, 0.0f, 1.0f, "Alpha %.2f");
float dummy_color[3] = { node->Pos.x / ImGui::GetWindowWidth(), node->Pos.y / ImGui::GetWindowHeight(), fmodf((float)node->ID * 0.5f, 1.0f) };
ImGui::ColorEdit3("##color", &dummy_color[0]);
ImGui::EndGroup();
// Save the size of what we have emitted and weither any of the widgets are being used
bool node_widgets_active = (!old_any_active && ImGui::IsAnyItemActive());
node->Size = ImGui::GetItemRectSize() + NODE_WINDOW_PADDING + NODE_WINDOW_PADDING;
ImVec2 node_rect_max = node_rect_min + node->Size;
// Display node box
draw_list->ChannelsSetCurrent(0); // Background
ImGui::SetCursorScreenPos(node_rect_min);
ImGui::InvisibleButton("node", node->Size);
if (ImGui::IsItemHovered())
{
node_hovered_in_scene = node->ID;
open_context_menu |= ImGui::IsMouseClicked(1);
}
bool node_moving_active = ImGui::IsItemActive();
if (node_widgets_active || node_moving_active)
node_selected = node->ID;
if (node_moving_active && ImGui::IsMouseDragging(0))
node->Pos = node->Pos + ImGui::GetIO().MouseDelta;
ImU32 node_bg_color = (node_hovered_in_list == node->ID || node_hovered_in_scene == node->ID || (node_hovered_in_list == -1 && node_selected == node->ID)) ? ImColor(75,75,75) : ImColor(60,60,60);
draw_list->AddRectFilled(node_rect_min, node_rect_max, node_bg_color, 4.0f);
draw_list->AddRect(node_rect_min, node_rect_max, ImColor(100,100,100), 4.0f);
for (int slot_idx = 0; slot_idx < node->InputsCount; slot_idx++)
draw_list->AddCircleFilled(offset + node->GetInputSlotPos(slot_idx), NODE_SLOT_RADIUS, ImColor(150,150,150,150));
for (int slot_idx = 0; slot_idx < node->OutputsCount; slot_idx++)
draw_list->AddCircleFilled(offset + node->GetOutputSlotPos(slot_idx), NODE_SLOT_RADIUS, ImColor(150,150,150,150));
ImGui::PopID();
}
draw_list->ChannelsMerge();
// Open context menu
if (!ImGui::IsAnyItemHovered() && ImGui::IsMouseHoveringWindow() && ImGui::IsMouseClicked(1))
{
node_selected = node_hovered_in_list = node_hovered_in_scene = -1;
open_context_menu = true;
}
if (open_context_menu)
{
ImGui::OpenPopup("context_menu");
if (node_hovered_in_list != -1)
node_selected = node_hovered_in_list;
if (node_hovered_in_scene != -1)
node_selected = node_hovered_in_scene;
}
// Draw context menu
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8,8));
if (ImGui::BeginPopup("context_menu"))
{
Node* node = node_selected != -1 ? &nodes[node_selected] : NULL;
ImVec2 scene_pos = ImGui::GetMousePosOnOpeningCurrentPopup() - offset;
if (node)
{
ImGui::Text("Node '%s'", node->Name);
ImGui::Separator();
if (ImGui::MenuItem("Rename..", NULL, false, false)) {}
if (ImGui::MenuItem("Delete", NULL, false, false)) {}
if (ImGui::MenuItem("Copy", NULL, false, false)) {}
}
else
{
if (ImGui::MenuItem("Add")) { nodes.push_back(Node(nodes.Size, "New node", scene_pos, 0.5f, 2, 2)); }
if (ImGui::MenuItem("Paste", NULL, false, false)) {}
}
ImGui::EndPopup();
}
ImGui::PopStyleVar();
// Scrolling
if (ImGui::IsWindowHovered() && !ImGui::IsAnyItemActive() && ImGui::IsMouseDragging(2, 0.0f))
scrolling = scrolling - ImGui::GetIO().MouseDelta;
ImGui::PopItemWidth();
ImGui::EndChild();
ImGui::PopStyleColor();
ImGui::PopStyleVar(2);
ImGui::EndGroup();
ImGui::End();
}

View file

@ -251,8 +251,16 @@ struct OcornutImguiContext
ImGui::NewFrame(); ImGui::NewFrame();
//ImGui::ShowTestWindow(); //Debug only. #if 0
// ImGui::ShowTestWindow(); //Debug only.
#endif // 0
#if 0
extern void ShowExampleAppCustomNodeGraph(bool* opened);
bool opened = true;
ShowExampleAppCustomNodeGraph(&opened);
#endif // 0
#if defined(SCI_NAMESPACE) && 0 #if defined(SCI_NAMESPACE) && 0
bool opened = true; bool opened = true;
ImGuiScintilla("Scintilla Editor", &opened, ImVec2(640.0f, 480.0f) ); ImGuiScintilla("Scintilla Editor", &opened, ImVec2(640.0f, 480.0f) );