Merge branch 'master' into viewport

# Conflicts:
#	examples/imgui_impl_opengl2.cpp
#	examples/imgui_impl_opengl2.h
#	examples/imgui_impl_opengl3.cpp
#	examples/imgui_impl_opengl3.h
#	imgui.cpp
#	imgui.h
This commit is contained in:
omar
2018-06-14 15:30:15 +02:00
15 changed files with 598 additions and 187 deletions

152
imgui.cpp
View File

@ -2200,6 +2200,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
if (g.ActiveIdIsJustActivated)
{
g.ActiveIdTimer = 0.0f;
g.ActiveIdValueChanged = false;
if (id != 0)
{
g.LastActiveId = id;
@ -2217,12 +2218,6 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
}
}
ImGuiID ImGui::GetActiveID()
{
ImGuiContext& g = *GImGui;
return g.ActiveId;
}
void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
{
ImGuiContext& g = *GImGui;
@ -2269,6 +2264,17 @@ void ImGui::KeepAliveID(ImGuiID id)
ImGuiContext& g = *GImGui;
if (g.ActiveId == id)
g.ActiveIdIsAlive = true;
if (g.ActiveIdPreviousFrame == id)
g.ActiveIdPreviousFrameIsAlive = true;
}
void ImGui::MarkItemValueChanged(ImGuiID id)
{
// This marking is solely to be able to provide info for IsItemDeactivatedAfterChange().
// ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need need to fill the data.
ImGuiContext& g = *GImGui;
IM_ASSERT(g.ActiveId == id || g.ActiveId == 0);
g.ActiveIdValueChanged = true;
}
static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
@ -2700,7 +2706,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
return false;
if (!IsMouseHoveringRect(bb.Min, bb.Max))
return false;
if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_Default))
if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
return false;
if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
return false;
@ -4196,7 +4202,9 @@ void ImGui::NewFrame()
g.ActiveIdTimer += g.IO.DeltaTime;
g.LastActiveIdTimer += g.IO.DeltaTime;
g.ActiveIdPreviousFrame = g.ActiveId;
g.ActiveIdIsAlive = false;
g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
g.ActiveIdPreviousFrameValueChanged = g.ActiveIdValueChanged;
g.ActiveIdIsAlive = g.ActiveIdPreviousFrameIsAlive = false;
g.ActiveIdIsJustActivated = false;
if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
g.ScalarAsInputTextId = 0;
@ -4456,7 +4464,7 @@ void ImGui::Shutdown(ImGuiContext* context)
g.NavWindow = NULL;
g.HoveredWindow = NULL;
g.HoveredRootWindow = NULL;
g.ActiveIdWindow = NULL;
g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
g.MovingWindow = NULL;
g.ColorModifiers.clear();
g.StyleModifiers.clear();
@ -5617,6 +5625,19 @@ bool ImGui::IsItemActive()
return false;
}
bool ImGui::IsItemDeactivated()
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
}
bool ImGui::IsItemDeactivatedAfterChange()
{
ImGuiContext& g = *GImGui;
return IsItemDeactivated() && (g.ActiveIdPreviousFrameValueChanged || (g.ActiveId == 0 && g.ActiveIdValueChanged));
}
bool ImGui::IsItemFocused()
{
ImGuiContext& g = *GImGui;
@ -5625,7 +5646,7 @@ bool ImGui::IsItemFocused()
bool ImGui::IsItemClicked(int mouse_button)
{
return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_Default);
return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
}
bool ImGui::IsAnyItemHovered()
@ -8963,6 +8984,8 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags
flags |= ImGuiButtonFlags_Repeat;
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
if (pressed)
MarkItemValueChanged(id);
// Render
const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
@ -9245,7 +9268,7 @@ void ImGui::LogButtons()
bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags)
{
if (flags & ImGuiTreeNodeFlags_Leaf)
return false;
return true;
// We only write to the tree storage if the user clicks (or explicitly use SetNextTreeNode*** functions)
ImGuiContext& g = *GImGui;
@ -10268,6 +10291,8 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, co
// Actual slider behavior + render grab
ItemSize(total_bb, style.FramePadding.y);
const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power);
if (value_changed)
MarkItemValueChanged(id);
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
char value_buf[64];
@ -10321,7 +10346,9 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
}
// Actual slider behavior + render grab
bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_Vertical);
const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_Vertical);
if (value_changed)
MarkItemValueChanged(id);
// Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
// For the vertical slider we allow centered text to overlap the frame padding
@ -10598,6 +10625,8 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, floa
// Actual drag behavior
ItemSize(total_bb, style.FramePadding.y);
const bool value_changed = DragBehavior(id, data_type, v, v_speed, v_min, v_max, format, power);
if (value_changed)
MarkItemValueChanged(id);
// Draw frame
const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
@ -10684,7 +10713,6 @@ bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_cu
TextUnformatted(label, FindRenderedTextEnd(label));
EndGroup();
PopID();
return value_changed;
}
@ -10947,7 +10975,10 @@ bool ImGui::Checkbox(const char* label, bool* v)
bool hovered, held;
bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
if (pressed)
{
*v = !(*v);
MarkItemValueChanged(id);
}
RenderNavHighlight(total_bb, id);
RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
@ -11015,6 +11046,8 @@ bool ImGui::RadioButton(const char* label, bool active)
bool hovered, held;
bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
if (pressed)
MarkItemValueChanged(id);
RenderNavHighlight(total_bb, id);
window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16);
@ -11043,9 +11076,7 @@ bool ImGui::RadioButton(const char* label, int* v, int v_button)
{
const bool pressed = RadioButton(label, *v == v_button);
if (pressed)
{
*v = v_button;
}
return pressed;
}
@ -11326,7 +11357,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;
if (is_multiline) // Open group before calling GetID() because groups tracks id created during their spawn
if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope,
BeginGroup();
const ImGuiID id = window->GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true);
@ -11916,6 +11947,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2
if (label_size.x > 0)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
if (value_changed)
MarkItemValueChanged(id);
if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
return enter_pressed;
else
@ -12038,7 +12072,6 @@ bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* v, in
TextUnformatted(label, FindRenderedTextEnd(label));
EndGroup();
return value_changed;
}
@ -12381,6 +12414,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
g.NavDisableHighlight = true;
SetNavID(id, window->DC.NavLayerCurrent);
}
if (pressed)
MarkItemValueChanged(id);
// Render
if (hovered || selected)
@ -12416,6 +12451,7 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags
return false;
}
// FIXME: Rename to BeginListBox()
// Helper to calculate the size of a listbox and display a label on the right.
// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty"
bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
@ -12443,6 +12479,7 @@ bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
return true;
}
// FIXME: Rename to BeginListBox()
bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items)
{
// Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
@ -12459,6 +12496,7 @@ bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_item
return ListBoxHeader(label, size);
}
// FIXME: Rename to EndListBox()
void ImGui::ListBoxFooter()
{
ImGuiWindow* parent_window = GetCurrentWindow()->ParentWindow;
@ -12606,7 +12644,7 @@ bool ImGui::BeginMenuBar()
return false;
IM_ASSERT(!window->DC.MenuBarAppending);
BeginGroup(); // Save position
BeginGroup(); // Backup position on layer 0
PushID("##menubar");
// We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect.
@ -12658,7 +12696,7 @@ void ImGui::EndMenuBar()
PopID();
window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->MenuBarRect().Min.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos.
window->DC.GroupStack.back().AdvanceCursor = false;
EndGroup();
EndGroup(); // Restore position on layer 0
window->DC.LayoutType = ImGuiLayoutType_Vertical;
window->DC.NavLayerCurrent--;
window->DC.NavLayerCurrentMask >>= 1;
@ -12970,6 +13008,9 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered)
ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf));
if (pressed)
MarkItemValueChanged(id);
return pressed;
}
@ -13264,6 +13305,9 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window)
window->DC.LastItemId = g.ActiveId;
if (value_changed)
MarkItemValueChanged(window->DC.LastItemId);
return value_changed;
}
@ -13587,12 +13631,18 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
}
EndGroup();
if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0)
value_changed = false;
if (value_changed)
MarkItemValueChanged(window->DC.LastItemId);
PopID();
return value_changed && memcmp(backup_initial_col, col, components * sizeof(float));
return value_changed;
}
// Horizontal separating line.
// Horizontal/vertical separating line
void ImGui::Separator()
{
ImGuiWindow* window = GetCurrentWindow();
@ -13600,9 +13650,8 @@ void ImGui::Separator()
return;
ImGuiContext& g = *GImGui;
ImGuiSeparatorFlags flags = 0;
if ((flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)) == 0)
flags |= (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal;
// Those flags should eventually be overridable by the user
ImGuiSeparatorFlags 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)
{
@ -13697,6 +13746,8 @@ bool ImGui::SplitterBehavior(ImGuiID id, const ImRect& bb, ImGuiAxis axis, float
*size1 += mouse_delta;
*size2 -= mouse_delta;
bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta));
MarkItemValueChanged(id);
}
// Render
@ -13740,6 +13791,7 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
// Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
void ImGui::BeginGroup()
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
@ -13751,21 +13803,21 @@ void ImGui::BeginGroup()
group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight;
group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
group_data.BackupLogLinePosY = window->DC.LogLinePosY;
group_data.BackupActiveIdIsAlive = GImGui->ActiveIdIsAlive;
group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
group_data.AdvanceCursor = true;
window->DC.GroupOffsetX = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffsetX;
window->DC.IndentX = window->DC.GroupOffsetX;
window->DC.CursorMaxPos = window->DC.CursorPos;
window->DC.CurrentLineHeight = 0.0f;
window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
}
void ImGui::EndGroup()
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls
ImGuiGroupData& group_data = window->DC.GroupStack.back();
@ -13775,11 +13827,11 @@ void ImGui::EndGroup()
window->DC.CursorPos = group_data.BackupCursorPos;
window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight;
window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
window->DC.IndentX = group_data.BackupIndentX;
window->DC.GroupOffsetX = group_data.BackupGroupOffsetX;
window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight;
window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
if (group_data.AdvanceCursor)
{
@ -13788,11 +13840,13 @@ void ImGui::EndGroup()
ItemAdd(group_bb, 0);
}
// 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)
// If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group.
// It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but put a little more burden on individual widgets.
// (and if you grep for LastItemId you'll notice it is only used in that context.
if (!group_data.BackupActiveIdIsAlive && g.ActiveIdIsAlive && g.ActiveId) // && g.ActiveIdWindow->RootWindow == window->RootWindow)
window->DC.LastItemId = g.ActiveId;
else if (!group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive) // && g.ActiveIdPreviousFrameWindow->RootWindow == window->RootWindow)
window->DC.LastItemId = g.ActiveIdPreviousFrame;
window->DC.LastItemRect = group_bb;
window->DC.GroupStack.pop_back();
@ -14217,6 +14271,7 @@ void ImGui::TreePop()
}
window->DC.TreeDepthMayJumpToParentOnPop &= (1 << window->DC.TreeDepth) - 1;
IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much.
PopID();
}
@ -14565,42 +14620,43 @@ static const char* GetClipboardTextFn_DefaultImpl(void*)
{
static ImVector<char> buf_local;
buf_local.clear();
if (!OpenClipboard(NULL))
if (!::OpenClipboard(NULL))
return NULL;
HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT);
HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
if (wbuf_handle == NULL)
{
CloseClipboard();
::CloseClipboard();
return NULL;
}
if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle))
if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle))
{
int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
buf_local.resize(buf_len);
ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
}
GlobalUnlock(wbuf_handle);
CloseClipboard();
::GlobalUnlock(wbuf_handle);
::CloseClipboard();
return buf_local.Data;
}
static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
{
if (!OpenClipboard(NULL))
if (!::OpenClipboard(NULL))
return;
const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
if (wbuf_handle == NULL)
{
CloseClipboard();
::CloseClipboard();
return;
}
ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle);
ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle);
ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
GlobalUnlock(wbuf_handle);
EmptyClipboard();
SetClipboardData(CF_UNICODETEXT, wbuf_handle);
CloseClipboard();
::GlobalUnlock(wbuf_handle);
::EmptyClipboard();
if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
::GlobalFree(wbuf_handle);
::CloseClipboard();
}
#else