(Breaking) Internals: added ImGuiItemFlags param to ItemHoverable(), so it can be called from ButtonBehavior() not following an ItemAdd().

This also allow moving AllowOverlap logic from ButtonBehavior() to ItemHoverable(), allowing other widgets to honor it. (#6512, #3909, #517)
This commit is contained in:
ocornut 2023-06-28 13:47:57 +02:00
parent 10c7709f30
commit 4dee919bc0
4 changed files with 22 additions and 21 deletions

View File

@ -4038,7 +4038,8 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
} }
// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered(). // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) // (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call)
bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* window = g.CurrentWindow;
@ -4053,7 +4054,6 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
return false; return false;
// Done with rectangle culling so we can perform heavier checks now. // Done with rectangle culling so we can perform heavier checks now.
ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags);
if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None)) if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
{ {
g.HoveredIdDisabled = true; g.HoveredIdDisabled = true;
@ -12360,7 +12360,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
// Rely on keeping other window->LastItemXXX fields intact. // Rely on keeping other window->LastItemXXX fields intact.
source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect); source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect);
KeepAliveID(source_id); KeepAliveID(source_id);
bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id); bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.InFlags);
if (is_hovered && g.IO.MouseClicked[mouse_button]) if (is_hovered && g.IO.MouseClicked[mouse_button])
{ {
SetActiveID(source_id, window); SetActiveID(source_id, window);

View File

@ -2876,7 +2876,7 @@ namespace ImGui
IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f); IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f);
inline void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f) { ItemSize(bb.GetSize(), text_baseline_y); } // FIXME: This is a misleading API since we expect CursorPos to be bb.Min. inline void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f) { ItemSize(bb.GetSize(), text_baseline_y); } // FIXME: This is a misleading API since we expect CursorPos to be bb.Min.
IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0); IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0);
IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id); IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags);
IMGUI_API bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags = 0); IMGUI_API bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags = 0);
IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id);
IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect);

View File

@ -976,7 +976,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight)); const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight));
const ImGuiID backup_active_id = g.ActiveId; const ImGuiID backup_active_id = g.ActiveId;
g.ActiveId = 0; g.ActiveId = 0;
const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0); const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0, ImGuiItemFlags_None);
g.ActiveId = backup_active_id; g.ActiveId = backup_active_id;
// [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column // [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column

View File

@ -493,11 +493,12 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
flags |= ImGuiButtonFlags_PressedOnDefault_; flags |= ImGuiButtonFlags_PressedOnDefault_;
// Default behavior inherited from item flags // Default behavior inherited from item flags
const ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags); // Note that _both_ ButtonFlags and ItemFlags are valid sources, so copy one into the item_flags and only check that.
if (item_flags & ImGuiItemFlags_ButtonRepeat) ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags);
flags |= ImGuiButtonFlags_Repeat; if (flags & ImGuiButtonFlags_AllowOverlap)
if (item_flags & ImGuiItemflags_AllowOverlap) item_flags |= ImGuiItemflags_AllowOverlap;
flags |= ImGuiButtonFlags_AllowOverlap; if (flags & ImGuiButtonFlags_Repeat)
item_flags |= ImGuiItemFlags_ButtonRepeat;
ImGuiWindow* backup_hovered_window = g.HoveredWindow; ImGuiWindow* backup_hovered_window = g.HoveredWindow;
const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window; const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window;
@ -511,7 +512,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
#endif #endif
bool pressed = false; bool pressed = false;
bool hovered = ItemHoverable(bb, id); bool hovered = ItemHoverable(bb, id, item_flags);
// Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button
if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers))
@ -531,7 +532,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
g.HoveredWindow = backup_hovered_window; g.HoveredWindow = backup_hovered_window;
// AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one. // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one.
if (flags & ImGuiButtonFlags_AllowOverlap) if (item_flags & ImGuiItemflags_AllowOverlap)
{ {
if (hovered && g.HoveredIdPreviousFrame != id) if (hovered && g.HoveredIdPreviousFrame != id)
hovered = false; hovered = false;
@ -587,7 +588,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
{ {
if (mouse_button_released != -1) if (mouse_button_released != -1)
{ {
const bool has_repeated_at_least_once = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior const bool has_repeated_at_least_once = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior
if (!has_repeated_at_least_once) if (!has_repeated_at_least_once)
pressed = true; pressed = true;
if (!(flags & ImGuiButtonFlags_NoNavFocus)) if (!(flags & ImGuiButtonFlags_NoNavFocus))
@ -598,7 +599,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
// 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above).
// Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.
if (g.ActiveId == id && (flags & ImGuiButtonFlags_Repeat)) if (g.ActiveId == id && (item_flags & ImGuiItemFlags_ButtonRepeat))
if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, test_owner_id, ImGuiInputFlags_Repeat)) if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, test_owner_id, ImGuiInputFlags_Repeat))
pressed = true; pressed = true;
} }
@ -616,7 +617,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
{ {
bool nav_activated_by_code = (g.NavActivateId == id); bool nav_activated_by_code = (g.NavActivateId == id);
bool nav_activated_by_inputs = (g.NavActivatePressedId == id); bool nav_activated_by_inputs = (g.NavActivatePressedId == id);
if (!nav_activated_by_inputs && (flags & ImGuiButtonFlags_Repeat)) if (!nav_activated_by_inputs && (item_flags & ImGuiItemFlags_ButtonRepeat))
{ {
// Avoid pressing multiple keys from triggering excessive amount of repeat events // Avoid pressing multiple keys from triggering excessive amount of repeat events
const ImGuiKeyData* key1 = GetKeyData(ImGuiKey_Space); const ImGuiKeyData* key1 = GetKeyData(ImGuiKey_Space);
@ -663,7 +664,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
{ {
// Report as pressed when releasing the mouse (this is the most common path) // Report as pressed when releasing the mouse (this is the most common path)
bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2; bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2;
bool is_repeating_already = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps <on release> bool is_repeating_already = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps <on release>
bool is_button_avail_or_owned = TestKeyOwner(MouseButtonToKey(mouse_button), test_owner_id); bool is_button_avail_or_owned = TestKeyOwner(MouseButtonToKey(mouse_button), test_owner_id);
if (!is_double_click_release && !is_repeating_already && is_button_avail_or_owned) if (!is_double_click_release && !is_repeating_already && is_button_avail_or_owned)
pressed = true; pressed = true;
@ -2421,7 +2422,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
if (format == NULL) if (format == NULL)
format = DataTypeGetInfo(data_type)->PrintFmt; format = DataTypeGetInfo(data_type)->PrintFmt;
const bool hovered = ItemHoverable(frame_bb, id); const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
if (!temp_input_is_active) if (!temp_input_is_active)
{ {
@ -3014,7 +3015,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
if (format == NULL) if (format == NULL)
format = DataTypeGetInfo(data_type)->PrintFmt; format = DataTypeGetInfo(data_type)->PrintFmt;
const bool hovered = ItemHoverable(frame_bb, id); const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);
bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
if (!temp_input_is_active) if (!temp_input_is_active)
{ {
@ -3181,7 +3182,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
if (format == NULL) if (format == NULL)
format = DataTypeGetInfo(data_type)->PrintFmt; format = DataTypeGetInfo(data_type)->PrintFmt;
const bool hovered = ItemHoverable(frame_bb, id); const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);
const bool clicked = hovered && IsMouseClicked(0, id); const bool clicked = hovered && IsMouseClicked(0, id);
if (clicked || g.NavActivateId == id) if (clicked || g.NavActivateId == id)
{ {
@ -4147,7 +4148,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
return false; return false;
item_status_flags = g.LastItemData.StatusFlags; item_status_flags = g.LastItemData.StatusFlags;
} }
const bool hovered = ItemHoverable(frame_bb, id); const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);
if (hovered) if (hovered)
g.MouseCursor = ImGuiMouseCursor_TextInput; g.MouseCursor = ImGuiMouseCursor_TextInput;
@ -6714,7 +6715,7 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get
ItemSize(total_bb, style.FramePadding.y); ItemSize(total_bb, style.FramePadding.y);
if (!ItemAdd(total_bb, 0, &frame_bb)) if (!ItemAdd(total_bb, 0, &frame_bb))
return -1; return -1;
const bool hovered = ItemHoverable(frame_bb, id); const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);
// Determine scale from values if not specified // Determine scale from values if not specified
if (scale_min == FLT_MAX || scale_max == FLT_MAX) if (scale_min == FLT_MAX || scale_max == FLT_MAX)