Internals: refactor: mouse input functions into their own section.

This commit is contained in:
ocornut 2022-01-04 14:53:30 +01:00
parent 206b9ead8f
commit 28eabcb099
1 changed files with 242 additions and 232 deletions

474
imgui.cpp
View File

@ -70,6 +70,7 @@ CODE
// [SECTION] STYLING
// [SECTION] RENDER HELPERS
// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
// [SECTION] INPUTS
// [SECTION] ERROR CHECKING
// [SECTION] LAYOUT
// [SECTION] SCROLLING
@ -4767,238 +4768,6 @@ static void FindHoveredWindow()
g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window;
}
// Test if mouse cursor is hovering given rectangle
// NB- Rectangle is clipped by our current clip setting
// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
{
ImGuiContext& g = *GImGui;
// Clip
ImRect rect_clipped(r_min, r_max);
if (clip)
rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
// Expand for touch input
const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
if (!rect_for_touch.Contains(g.IO.MousePos))
return false;
return true;
}
int ImGui::GetKeyIndex(ImGuiKey imgui_key)
{
IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
ImGuiContext& g = *GImGui;
return g.IO.KeyMap[imgui_key];
}
// Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]!
// Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]!
bool ImGui::IsKeyDown(int user_key_index)
{
if (user_key_index < 0)
return false;
ImGuiContext& g = *GImGui;
IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
return g.IO.KeysDown[user_key_index];
}
// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
// t1 = current time (e.g.: g.Time)
// An event is triggered at:
// t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N
int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
{
if (t1 == 0.0f)
return 1;
if (t0 >= t1)
return 0;
if (repeat_rate <= 0.0f)
return (t0 < repeat_delay) && (t1 >= repeat_delay);
const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
const int count = count_t1 - count_t0;
return count;
}
int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
{
ImGuiContext& g = *GImGui;
if (key_index < 0)
return 0;
IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
const float t = g.IO.KeysDownDuration[key_index];
return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
}
bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
{
ImGuiContext& g = *GImGui;
if (user_key_index < 0)
return false;
IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
const float t = g.IO.KeysDownDuration[user_key_index];
if (t == 0.0f)
return true;
if (repeat && t > g.IO.KeyRepeatDelay)
return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
return false;
}
bool ImGui::IsKeyReleased(int user_key_index)
{
ImGuiContext& g = *GImGui;
if (user_key_index < 0) return false;
IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
}
bool ImGui::IsMouseDown(ImGuiMouseButton button)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
return g.IO.MouseDown[button];
}
bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
const float t = g.IO.MouseDownDuration[button];
if (t == 0.0f)
return true;
if (repeat && t > g.IO.KeyRepeatDelay)
{
// FIXME: 2019/05/03: Our old repeat code was wrong here and led to doubling the repeat rate, which made it an ok rate for repeat on mouse hold.
int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
if (amount > 0)
return true;
}
return false;
}
bool ImGui::IsMouseReleased(ImGuiMouseButton button)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
return g.IO.MouseReleased[button];
}
bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
return g.IO.MouseClickedCount[button] == 2;
}
int ImGui::GetMouseClickedCount(ImGuiMouseButton button)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
return g.IO.MouseClickedCount[button];
}
// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
// [Internal] This doesn't test if the button is pressed
bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
if (lock_threshold < 0.0f)
lock_threshold = g.IO.MouseDragThreshold;
return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
}
bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
if (!g.IO.MouseDown[button])
return false;
return IsMouseDragPastThreshold(button, lock_threshold);
}
ImVec2 ImGui::GetMousePos()
{
ImGuiContext& g = *GImGui;
return g.IO.MousePos;
}
// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
{
ImGuiContext& g = *GImGui;
if (g.BeginPopupStack.Size > 0)
return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
return g.IO.MousePos;
}
// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
{
// The assert is only to silence a false-positive in XCode Static Analysis.
// Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions).
IM_ASSERT(GImGui != NULL);
const float MOUSE_INVALID = -256000.0f;
ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
}
bool ImGui::IsAnyMouseDown()
{
ImGuiContext& g = *GImGui;
for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
if (g.IO.MouseDown[n])
return true;
return false;
}
// Return the delta from the initial clicking position while the mouse button is clicked or was just released.
// This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.
ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
if (lock_threshold < 0.0f)
lock_threshold = g.IO.MouseDragThreshold;
if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
return g.IO.MousePos - g.IO.MouseClickedPos[button];
return ImVec2(0.0f, 0.0f);
}
void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
// NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
g.IO.MouseClickedPos[button] = g.IO.MousePos;
}
ImGuiMouseCursor ImGui::GetMouseCursor()
{
return GImGui->MouseCursor;
}
void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
{
GImGui->MouseCursor = cursor_type;
}
void ImGui::CaptureKeyboardFromApp(bool capture)
{
GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
}
void ImGui::CaptureMouseFromApp(bool capture)
{
GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
}
bool ImGui::IsItemActive()
{
ImGuiContext& g = *GImGui;
@ -7460,6 +7229,247 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
}
//-----------------------------------------------------------------------------
// [SECTION] INPUTS
//-----------------------------------------------------------------------------
// Test if mouse cursor is hovering given rectangle
// NB- Rectangle is clipped by our current clip setting
// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
{
ImGuiContext& g = *GImGui;
// Clip
ImRect rect_clipped(r_min, r_max);
if (clip)
rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
// Expand for touch input
const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
if (!rect_for_touch.Contains(g.IO.MousePos))
return false;
return true;
}
int ImGui::GetKeyIndex(ImGuiKey imgui_key)
{
IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
ImGuiContext& g = *GImGui;
return g.IO.KeyMap[imgui_key];
}
// Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]!
// Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]!
bool ImGui::IsKeyDown(int user_key_index)
{
if (user_key_index < 0)
return false;
ImGuiContext& g = *GImGui;
IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
return g.IO.KeysDown[user_key_index];
}
// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
// t1 = current time (e.g.: g.Time)
// An event is triggered at:
// t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N
int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
{
if (t1 == 0.0f)
return 1;
if (t0 >= t1)
return 0;
if (repeat_rate <= 0.0f)
return (t0 < repeat_delay) && (t1 >= repeat_delay);
const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
const int count = count_t1 - count_t0;
return count;
}
int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
{
ImGuiContext& g = *GImGui;
if (key_index < 0)
return 0;
IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
const float t = g.IO.KeysDownDuration[key_index];
return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
}
bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
{
ImGuiContext& g = *GImGui;
if (user_key_index < 0)
return false;
IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
const float t = g.IO.KeysDownDuration[user_key_index];
if (t == 0.0f)
return true;
if (repeat && t > g.IO.KeyRepeatDelay)
return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
return false;
}
bool ImGui::IsKeyReleased(int user_key_index)
{
ImGuiContext& g = *GImGui;
if (user_key_index < 0) return false;
IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
}
bool ImGui::IsMouseDown(ImGuiMouseButton button)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
return g.IO.MouseDown[button];
}
bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
const float t = g.IO.MouseDownDuration[button];
if (t == 0.0f)
return true;
if (repeat && t > g.IO.KeyRepeatDelay)
{
// FIXME: 2019/05/03: Our old repeat code was wrong here and led to doubling the repeat rate, which made it an ok rate for repeat on mouse hold.
int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
if (amount > 0)
return true;
}
return false;
}
bool ImGui::IsMouseReleased(ImGuiMouseButton button)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
return g.IO.MouseReleased[button];
}
bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
return g.IO.MouseClickedCount[button] == 2;
}
int ImGui::GetMouseClickedCount(ImGuiMouseButton button)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
return g.IO.MouseClickedCount[button];
}
// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
// [Internal] This doesn't test if the button is pressed
bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
if (lock_threshold < 0.0f)
lock_threshold = g.IO.MouseDragThreshold;
return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
}
bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
if (!g.IO.MouseDown[button])
return false;
return IsMouseDragPastThreshold(button, lock_threshold);
}
ImVec2 ImGui::GetMousePos()
{
ImGuiContext& g = *GImGui;
return g.IO.MousePos;
}
// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
{
ImGuiContext& g = *GImGui;
if (g.BeginPopupStack.Size > 0)
return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
return g.IO.MousePos;
}
// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
{
// The assert is only to silence a false-positive in XCode Static Analysis.
// Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions).
IM_ASSERT(GImGui != NULL);
const float MOUSE_INVALID = -256000.0f;
ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
}
bool ImGui::IsAnyMouseDown()
{
ImGuiContext& g = *GImGui;
for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
if (g.IO.MouseDown[n])
return true;
return false;
}
// Return the delta from the initial clicking position while the mouse button is clicked or was just released.
// This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.
ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
if (lock_threshold < 0.0f)
lock_threshold = g.IO.MouseDragThreshold;
if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
return g.IO.MousePos - g.IO.MouseClickedPos[button];
return ImVec2(0.0f, 0.0f);
}
void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
// NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
g.IO.MouseClickedPos[button] = g.IO.MousePos;
}
ImGuiMouseCursor ImGui::GetMouseCursor()
{
ImGuiContext& g = *GImGui;
return g.MouseCursor;
}
void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
{
ImGuiContext& g = *GImGui;
g.MouseCursor = cursor_type;
}
void ImGui::CaptureKeyboardFromApp(bool capture)
{
ImGuiContext& g = *GImGui;
g.WantCaptureKeyboardNextFrame = capture ? 1 : 0;
}
void ImGui::CaptureMouseFromApp(bool capture)
{
ImGuiContext& g = *GImGui;
g.WantCaptureMouseNextFrame = capture ? 1 : 0;
}
//-----------------------------------------------------------------------------
// [SECTION] ERROR CHECKING
//-----------------------------------------------------------------------------