Skip to content

Commit

Permalink
Added IsItemDeactivatedAfterChange() if the last item was active prev…
Browse files Browse the repository at this point in the history
…iously, isn't anymore, and during its active state modified a value. Note that you may still get false positive. (#820, #956, #1875)
  • Loading branch information
ocornut committed Jun 12, 2018
1 parent be4b8b5 commit d57fc7f
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 10 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ Other Changes:
- Each example still has its own main.cpp which you may refer you to understand how to initialize and glue everything together.
- Some frameworks (such as the Allegro, Marmalade) handle both the "platform" and "rendering" part, and your custom engine may as well.
- Read examples/README.txt for details.
- Added IsItemDeactivated() to query if the last item was active previously but isn't anymore. Useful for Undo/Redo patterns. (#820, #956, #1875)
- Added IsItemDeactivated() to query if the last item was active previously and isn't anymore. Useful for Undo/Redo patterns. (#820, #956, #1875)
- Added IsItemDeactivatedAfterChange() if the last item was active previously, isn't anymore, and during its active state modified a value.
Note that you may still get false positive (e.g. drag value and while holding return on the same value). (#820, #956, #1875)
- Nav: Added support for PageUp/PageDown (explorer-style: first aim at bottom/top most item, when scroll a page worth of contents). (#787)
- Nav: To keep the navigated item in view we also attempt to scroll the parent window as well as the current window. (#787)
- TreeNode: Fixed nodes with ImGuiTreeNodeFlags_Leaf flag always returning true which was meaningless.
Expand Down
53 changes: 51 additions & 2 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2182,6 +2182,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
if (g.ActiveIdIsJustActivated)
{
g.ActiveIdTimer = 0.0f;
g.ActiveIdValueChanged = false;
if (id != 0)
{
g.LastActiveId = id;
Expand Down Expand Up @@ -2249,6 +2250,15 @@ void ImGui::KeepAliveID(ImGuiID 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)
{
// An active popup disable hovering on other windows (apart from its own children)
Expand Down Expand Up @@ -3716,6 +3726,7 @@ void ImGui::NewFrame()
g.LastActiveIdTimer += g.IO.DeltaTime;
g.ActiveIdPreviousFrame = g.ActiveId;
g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
g.ActiveIdPreviousFrameValueChanged = g.ActiveIdValueChanged;
g.ActiveIdIsAlive = g.ActiveIdPreviousFrameIsAlive = false;
g.ActiveIdIsJustActivated = false;
if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
Expand Down Expand Up @@ -4982,6 +4993,12 @@ bool ImGui::IsItemDeactivated()
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;
Expand Down Expand Up @@ -8022,6 +8039,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);
Expand Down Expand Up @@ -9327,6 +9346,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];
Expand Down Expand Up @@ -9380,7 +9401,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
Expand Down Expand Up @@ -9657,6 +9680,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);
Expand Down Expand Up @@ -10005,7 +10030,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);
Expand Down Expand Up @@ -10073,6 +10101,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);
Expand Down Expand Up @@ -10969,6 +10999,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
Expand Down Expand Up @@ -11433,6 +11466,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)
Expand Down Expand Up @@ -12025,6 +12060,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;
}

Expand Down Expand Up @@ -12319,6 +12357,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;
}

Expand Down Expand Up @@ -12642,9 +12683,15 @@ 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.
Expand Down Expand Up @@ -12752,6 +12799,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
Expand Down
3 changes: 2 additions & 1 deletion imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,8 @@ namespace ImGui
IMGUI_API bool IsItemFocused(); // is the last item focused for keyboard/gamepad navigation?
IMGUI_API bool IsItemClicked(int mouse_button = 0); // is the last item clicked? (e.g. button/node just clicked on) == IsMouseClicked(mouse_button) && IsItemHovered()
IMGUI_API bool IsItemVisible(); // is the last item visible? (items may be out of sight because of clipping/scrolling)
IMGUI_API bool IsItemDeactivated(); // was the last item just made inactive (item was previously active), useful for Undo/Redo patterns.
IMGUI_API bool IsItemDeactivated(); // was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that requires continuous editing.
IMGUI_API bool IsItemDeactivatedAfterChange(); // was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that requires continuous editing. Note that you may get false positives (some widgets such as Combo()/ListBox()/Selectable() will return true even when clicking an already selected item).
IMGUI_API bool IsAnyItemHovered();
IMGUI_API bool IsAnyItemActive();
IMGUI_API bool IsAnyItemFocused();
Expand Down
17 changes: 11 additions & 6 deletions imgui_demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1178,18 +1178,21 @@ void ImGui::ShowDemoWindow(bool* p_open)
// Display the value of IsItemHovered() and other common item state functions. Note that the flags can be combined.
// (because BulletText is an item itself and that would affect the output of IsItemHovered() we pass all state in a single call to simplify the code).
static int item_type = 1;
static bool b = false;
static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f };
ImGui::RadioButton("Text", &item_type, 0); ImGui::SameLine();
ImGui::RadioButton("Button", &item_type, 1); ImGui::SameLine();
ImGui::RadioButton("SliderFloat", &item_type, 2); ImGui::SameLine();
ImGui::RadioButton("ColorEdit4", &item_type, 3); ImGui::SameLine();
ImGui::RadioButton("ListBox", &item_type, 4);
ImGui::RadioButton("CheckBox", &item_type, 2); ImGui::SameLine();
ImGui::RadioButton("SliderFloat", &item_type, 3); ImGui::SameLine();
ImGui::RadioButton("ColorEdit4", &item_type, 4); ImGui::SameLine();
ImGui::RadioButton("ListBox", &item_type, 5);
bool ret = false;
if (item_type == 0) { ImGui::Text("ITEM: Text"); } // Testing text items with no identifier/interaction
if (item_type == 1) { ret = ImGui::Button("ITEM: Button"); } // Testing button
if (item_type == 2) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item
if (item_type == 3) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged)
if (item_type == 4) { const char* items[] = { "Apple", "Banana", "Cherry" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", &current, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); }
if (item_type == 2) { ret = ImGui::Checkbox("ITEM: CheckBox", &b); } // Testing checkbox
if (item_type == 3) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item
if (item_type == 4) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged)
if (item_type == 5) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", &current, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); }
ImGui::BulletText(
"Return value = %d\n"
"IsItemFocused() = %d\n"
Expand All @@ -1200,6 +1203,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
"IsItemHovered(_RectOnly) = %d\n"
"IsItemActive() = %d\n"
"IsItemDeactivated() = %d\n"
"IsItemDeactivatedAfterChange() = %d\n"
"IsItemVisible() = %d\n",
ret,
ImGui::IsItemFocused(),
Expand All @@ -1210,6 +1214,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly),
ImGui::IsItemActive(),
ImGui::IsItemDeactivated(),
ImGui::IsItemDeactivatedAfterChange(),
ImGui::IsItemVisible()
);

Expand Down
5 changes: 5 additions & 0 deletions imgui_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,9 @@ struct ImGuiContext
bool ActiveIdIsAlive; // Active widget has been seen this frame
bool ActiveIdIsJustActivated; // Set at the time of activation for one frame
bool ActiveIdAllowOverlap; // Active widget allows another widget to steal active id (generally for overlapping widgets, but not always)
bool ActiveIdValueChanged;
bool ActiveIdPreviousFrameIsAlive;
bool ActiveIdPreviousFrameValueChanged;
int ActiveIdAllowNavDirFlags; // Active widget allows using directional navigation (e.g. can activate a button and move away from it)
ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior)
ImGuiWindow* ActiveIdWindow;
Expand Down Expand Up @@ -765,7 +767,9 @@ struct ImGuiContext
ActiveIdIsAlive = false;
ActiveIdIsJustActivated = false;
ActiveIdAllowOverlap = false;
ActiveIdValueChanged = false;
ActiveIdPreviousFrameIsAlive = false;
ActiveIdPreviousFrameValueChanged = false;
ActiveIdAllowNavDirFlags = 0;
ActiveIdClickOffset = ImVec2(-1,-1);
ActiveIdWindow = ActiveIdPreviousFrameWindow = NULL;
Expand Down Expand Up @@ -1082,6 +1086,7 @@ namespace ImGui
IMGUI_API ImGuiID GetHoveredID();
IMGUI_API void SetHoveredID(ImGuiID id);
IMGUI_API void KeepAliveID(ImGuiID id);
IMGUI_API void MarkItemValueChanged(ImGuiID id);

IMGUI_API void ItemSize(const ImVec2& size, float text_offset_y = 0.0f);
IMGUI_API void ItemSize(const ImRect& bb, float text_offset_y = 0.0f);
Expand Down

0 comments on commit d57fc7f

Please sign in to comment.