Skip to content

Commit

Permalink
Added IsItemDeactivated() to query if the last item was active previo…
Browse files Browse the repository at this point in the history
…usly but isn't anymore. Useful for Undo/Redo patterns. (#820, #956, #1875)
  • Loading branch information
ocornut committed Jun 12, 2018
1 parent c725710 commit cd455a4
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ 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)
- 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
25 changes: 19 additions & 6 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2251,6 +2251,8 @@ void ImGui::KeepAliveID(ImGuiID id)
ImGuiContext& g = *GImGui;
if (g.ActiveId == id)
g.ActiveIdIsAlive = true;
if (g.ActiveIdPreviousFrame == id)
g.ActiveIdPreviousFrameIsAlive = true;
}

static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
Expand Down Expand Up @@ -3719,7 +3721,8 @@ 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.ActiveIdIsAlive = g.ActiveIdPreviousFrameIsAlive = false;
g.ActiveIdIsJustActivated = false;
if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
g.ScalarAsInputTextId = 0;
Expand Down Expand Up @@ -3940,7 +3943,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();
Expand Down Expand Up @@ -4978,6 +4981,13 @@ 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::IsItemFocused()
{
ImGuiContext& g = *GImGui;
Expand Down Expand Up @@ -12804,6 +12814,7 @@ void ImGui::BeginGroup()
group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
group_data.BackupLogLinePosY = window->DC.LogLinePosY;
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;
Expand Down Expand Up @@ -12839,11 +12850,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();
Expand Down
1 change: 1 addition & 0 deletions imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ 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(0) && IsItemHovered()
IMGUI_API bool IsItemVisible(); // is the last item visible? (aka not out of sight due to clipping/scrolling.)
IMGUI_API bool IsItemDeactivated(); // is the last item just made inactive (item was previously active), useful for Undo/Redo patterns.
IMGUI_API bool IsAnyItemHovered();
IMGUI_API bool IsAnyItemActive();
IMGUI_API bool IsAnyItemFocused();
Expand Down
12 changes: 9 additions & 3 deletions imgui_demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2028,7 +2028,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::TreePop();
}

if (ImGui::TreeNode("Focused & Hovered Test"))
if (ImGui::TreeNode("Active, Focused & Hovered Test"))
{
static bool embed_all_inside_a_child_window = false;
ImGui::Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window);
Expand Down Expand Up @@ -2068,16 +2068,22 @@ void ImGui::ShowDemoWindow(bool* p_open)
// Testing IsItemHovered() function (because BulletText is an item itself and that would affect the output of IsItemHovered, we pass all lines in a single items to shorten the code)
ImGui::Button("ITEM");
ImGui::BulletText(
"IsItemFocused() = %d\n"
"IsItemHovered() = %d\n"
"IsItemHovered(_AllowWhenBlockedByPopup) = %d\n"
"IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n"
"IsItemHovered(_AllowWhenOverlapped) = %d\n"
"IsItemhovered(_RectOnly) = %d\n",
"IsItemHovered(_RectOnly) = %d\n"
"IsItemActive() = %d\n"
"IsItemDeactivated() = %d\n",
ImGui::IsItemFocused(),
ImGui::IsItemHovered(),
ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),
ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlapped),
ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly));
ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly),
ImGui::IsItemActive(),
ImGui::IsItemDeactivated());

ImGui::BeginChild("child", ImVec2(0,50), true);
ImGui::Text("This is another child window for testing IsWindowHovered() flags.");
Expand Down
6 changes: 5 additions & 1 deletion imgui_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ struct ImGuiGroupData
float BackupCurrentLineTextBaseOffset;
float BackupLogLinePosY;
bool BackupActiveIdIsAlive;
bool BackupActiveIdPreviousFrameIsAlive;
bool AdvanceCursor;
};

Expand Down Expand Up @@ -624,9 +625,11 @@ 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 ActiveIdPreviousFrameIsAlive;
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;
ImGuiWindow* ActiveIdPreviousFrameWindow;
ImGuiInputSource ActiveIdSource; // Activating with mouse or nav (gamepad/keyboard)
ImGuiID LastActiveId; // Store the last non-zero ActiveId, useful for animation.
float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation.
Expand Down Expand Up @@ -762,9 +765,10 @@ struct ImGuiContext
ActiveIdIsAlive = false;
ActiveIdIsJustActivated = false;
ActiveIdAllowOverlap = false;
ActiveIdPreviousFrameIsAlive = false;
ActiveIdAllowNavDirFlags = 0;
ActiveIdClickOffset = ImVec2(-1,-1);
ActiveIdWindow = NULL;
ActiveIdWindow = ActiveIdPreviousFrameWindow = NULL;
ActiveIdSource = ImGuiInputSource_None;
LastActiveId = 0;
LastActiveIdTimer = 0.0f;
Expand Down

0 comments on commit cd455a4

Please sign in to comment.