Nav: first committed pass for manual moving and manual scrolling (after a bunch of attempts) (#323)

This commit is contained in:
ocornut 2016-07-30 17:18:34 +02:00
parent 4309b8c1ed
commit 04157da291
4 changed files with 86 additions and 16 deletions

View File

@ -662,6 +662,7 @@ static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true);
static void SetCurrentFont(ImFont* font);
static void SetCurrentWindow(ImGuiWindow* window);
static void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x);
static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y);
static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond);
static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond);
@ -2364,8 +2365,7 @@ static void NavUpdate()
IM_ASSERT(g.NavWindow);
// Scroll to keep newly navigated item fully into view
ImRect window_rect_rel(g.NavWindow->InnerRect.Min - g.NavWindow->Pos, g.NavWindow->InnerRect.Max - g.NavWindow->Pos);
window_rect_rel.Expand(1.0f);
ImRect window_rect_rel(g.NavWindow->InnerRect.Min - g.NavWindow->Pos - ImVec2(1,1), g.NavWindow->InnerRect.Max - g.NavWindow->Pos + ImVec2(1,1));
//g.OverlayDrawList.AddRect(g.NavWindow->Pos + window_rect_rel.Min, g.NavWindow->Pos + window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG]
if (g.NavLayer == 0 && !window_rect_rel.Contains(g.NavMoveResultRectRel))
{
@ -2394,6 +2394,7 @@ static void NavUpdate()
// Apply result from previous navigation directional move request
ImGui::SetActiveID(0);
SetNavIdMoveMouse(g.NavMoveResultId, g.NavMoveResultRectRel);
g.NavMoveFromClampedRefRect = false;
}
// Navigation windowing mode (change focus, move/resize window)
@ -2433,6 +2434,19 @@ static void NavUpdate()
g.NavWindowingDisplayAlpha = 1.0f;
}
// Move window
if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
{
const ImVec2 move_delta = ImGui::NavGetMovingDir(1);
if (move_delta.x != 0.0f || move_delta.y != 0.0f)
{
const float move_speed = ImFloor(600 * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
g.NavWindowingTarget->PosFloat += move_delta * move_speed;
if (!(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoSavedSettings))
MarkSettingsDirty();
}
}
if (!IsKeyDownMap(ImGuiKey_NavMenu))
{
// Apply actual focus only when releasing the NavMenu button (until then the window was merely rendered front-most)
@ -2537,16 +2551,44 @@ static void NavUpdate()
g.NavWindow = g.FocusedWindow;
}
// Fallback manual-scroll with NavUp/NavDown when window has no navigable item
if (g.FocusedWindow && !g.FocusedWindow->DC.NavLayerActiveFlags && g.FocusedWindow->DC.NavHasScroll && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNav) && g.NavMoveRequest && (g.NavMoveDir == ImGuiNavDir_Up || g.NavMoveDir == ImGuiNavDir_Down))
// Scrolling
if (g.FocusedWindow && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNav))
{
float scroll_speed = ImFloor(g.FocusedWindow->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
SetWindowScrollY(g.FocusedWindow, ImFloor(g.FocusedWindow->Scroll.y + ((g.NavMoveDir == ImGuiNavDir_Up) ? -1.0f : +1.0f) * scroll_speed));
// Fallback manual-scroll with NavUp/NavDown when window has no navigable item
const float scroll_speed = ImFloor(g.FocusedWindow->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
if (!g.FocusedWindow->DC.NavLayerActiveFlags && g.FocusedWindow->DC.NavHasScroll && g.NavMoveRequest && (g.NavMoveDir == ImGuiNavDir_Up || g.NavMoveDir == ImGuiNavDir_Down))
SetWindowScrollY(g.FocusedWindow, ImFloor(g.FocusedWindow->Scroll.y + ((g.NavMoveDir == ImGuiNavDir_Up) ? -1.0f : +1.0f) * scroll_speed));
// Manual scroll with NavScrollXXX keys
ImVec2 scroll_dir = ImGui::NavGetMovingDir(1, 1.0f/10.0f, 10.0f);
if (scroll_dir.x != 0.0f && g.NavWindow->ScrollbarX)
{
SetWindowScrollX(g.FocusedWindow, ImFloor(g.FocusedWindow->Scroll.x + scroll_dir.x * scroll_speed));
g.NavMoveFromClampedRefRect = true;
}
if (scroll_dir.y != 0.0f)
{
SetWindowScrollY(g.FocusedWindow, ImFloor(g.FocusedWindow->Scroll.y + scroll_dir.y * scroll_speed));
g.NavMoveFromClampedRefRect = true;
}
}
// Reset search
g.NavMoveResultId = 0;
g.NavMoveResultDistAxial = g.NavMoveResultDistBox = g.NavMoveResultDistCenter = FLT_MAX;
if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
{
// When we have manually scrolled and NavId is out of bounds, we clamp its bounding box (used for search) to the visible area to restart navigation within visible items
ImRect window_rect_rel(g.NavWindow->InnerRect.Min - g.NavWindow->Pos - ImVec2(1,1), g.NavWindow->InnerRect.Max - g.NavWindow->Pos + ImVec2(1,1));
if (!window_rect_rel.Contains(g.NavRefRectRel))
{
float pad = g.NavWindow->CalcFontSize() * 0.5f;
window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intend of starting navigation from first fully visible item
window_rect_rel.Clip(g.NavRefRectRel);
g.NavId = 0;
}
g.NavMoveFromClampedRefRect = false;
}
// For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items)
g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + g.NavRefRectRel.Min, g.NavWindow->Pos + g.NavRefRectRel.Max) : ImRect();
@ -4737,7 +4779,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us
if (g.NavWindowingTarget == window)
{
const float resize_speed = ImFloor(600 * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
nav_resize_delta = NavGetMovingDir() * resize_speed;
nav_resize_delta = NavGetMovingDir(0) * resize_speed;
held |= (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f); // For coloring
}
@ -5469,6 +5511,13 @@ ImVec2 ImGui::GetWindowPos()
return window->Pos;
}
static void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x)
{
window->DC.CursorMaxPos.x += window->Scroll.x; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it.
window->Scroll.x = new_scroll_x;
window->DC.CursorMaxPos.x -= window->Scroll.x;
}
static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
{
window->DC.CursorMaxPos.y += window->Scroll.y; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it.
@ -7043,13 +7092,28 @@ int ImGui::ParseFormatPrecision(const char* fmt, int default_precision)
return precision;
}
ImVec2 ImGui::NavGetMovingDir()
ImVec2 ImGui::NavGetMovingDir(int stick_no, float slow_factor, float fast_factor)
{
IM_ASSERT(stick_no >= 0 && stick_no < 2);
ImVec2 dir(0.0f, 0.0f);
if (IsKeyDownMap(ImGuiKey_NavLeft)) dir.x -= 1.0f;
if (IsKeyDownMap(ImGuiKey_NavRight)) dir.x += 1.0f;
if (IsKeyDownMap(ImGuiKey_NavUp)) dir.y -= 1.0f;
if (IsKeyDownMap(ImGuiKey_NavDown)) dir.y += 1.0f;
if (stick_no == 0)
{
if (IsKeyDownMap(ImGuiKey_NavLeft)) dir.x -= 1.0f;
if (IsKeyDownMap(ImGuiKey_NavRight)) dir.x += 1.0f;
if (IsKeyDownMap(ImGuiKey_NavUp)) dir.y -= 1.0f;
if (IsKeyDownMap(ImGuiKey_NavDown)) dir.y += 1.0f;
}
if (stick_no == 1)
{
if (IsKeyDownMap(ImGuiKey_NavScrollLeft)) dir.x -= 1.0f;
if (IsKeyDownMap(ImGuiKey_NavScrollRight)) dir.x += 1.0f;
if (IsKeyDownMap(ImGuiKey_NavScrollUp)) dir.y -= 1.0f;
if (IsKeyDownMap(ImGuiKey_NavScrollDown)) dir.y += 1.0f;
}
if (slow_factor != 0.0f && IsKeyDownMap(ImGuiKey_NavTweakSlower))
dir *= slow_factor;
if (fast_factor != 0.0f && IsKeyDownMap(ImGuiKey_NavTweakFaster))
dir *= fast_factor;
return dir;
}
@ -10458,7 +10522,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
}
ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %-4d %s vtx, tex = %p, clip_rect = (%.0f,%.0f)..(%.0f,%.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
if (show_clip_rects && ImGui::IsItemHovered())
if (show_clip_rects && (ImGui::IsItemHovered() || ImGui::IsItemFocused()))
{
ImRect clip_rect = pcmd->ClipRect;
ImRect vtxs_rect;
@ -10482,7 +10546,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
buf_p += sprintf(buf_p, "%s %04d { pos = (%8.2f,%8.2f), uv = (%.6f,%.6f), col = %08X }\n", (n == 0) ? "vtx" : " ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
}
ImGui::Selectable(buf, false);
if (ImGui::IsItemHovered())
if (ImGui::IsItemHovered() || ImGui::IsItemFocused())
overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f, false); // Add triangle without AA, more readable for large-thin triangle
}
ImGui::TreePop();

View File

@ -604,8 +604,13 @@ enum ImGuiKey_
ImGuiKey_NavRight, // e.g. Right arrow, D-Pad right
ImGuiKey_NavUp, // e.g. Up arrow, D-Pad up
ImGuiKey_NavDown, // e.g. Down arrow, D-Pad down
ImGuiKey_NavScrollLeft, // e.g. Analog left
ImGuiKey_NavScrollRight,// e.g. Analog right
ImGuiKey_NavScrollUp, // e.g. Analog up
ImGuiKey_NavScrollDown, // e.g. Analog down
ImGuiKey_NavTweakFaster,// e.g. Shift key, R-trigger
ImGuiKey_NavTweakSlower,// e.g. Alt key, L-trigger
ImGuiKey_NavLast_,
ImGuiKey_COUNT
};

View File

@ -1904,7 +1904,7 @@ static void ShowExampleAppConstrainedResize(bool* p_open)
static void ShowExampleAppFixedOverlay(bool* p_open)
{
ImGui::SetNextWindowPos(ImVec2(10,10));
if (!ImGui::Begin("Example: Fixed Overlay", p_open, ImVec2(0,0), 0.3f, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings))
if (!ImGui::Begin("Example: Fixed Overlay", p_open, ImVec2(0,0), 0.3f, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_NoNav))
{
ImGui::End();
return;

View File

@ -423,6 +423,7 @@ struct ImGuiContext
ImRect NavInitDefaultResultRectRel;
bool NavInitDefaultResultExplicit; // Whether the result was explicitly requested with SetItemDefaultFocus()
bool NavMoveRequest; // Move request for this frame
bool NavMoveFromClampedRefRect; // Set by manual scrolling, if we scroll to a point where NavId isn't visible we reset navigation from visible items
ImGuiNavDir NavMoveDir; // West/East/North/South
ImGuiID NavMoveResultId; // Best move request candidate
float NavMoveResultDistBox; // Best move request candidate box distance to current NavId
@ -784,7 +785,7 @@ namespace ImGui
IMGUI_API void OpenPopupEx(const char* str_id, bool reopen_existing);
IMGUI_API ImVec2 NavGetTweakDelta();
IMGUI_API ImVec2 NavGetMovingDir();
IMGUI_API ImVec2 NavGetMovingDir(int stick_no, float slow_factor = 0.0f, float fast_factor = 0.0f);
inline IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul) { ImVec4 c = GImGui->Style.Colors[idx]; c.w *= GImGui->Style.Alpha * alpha_mul; return ImGui::ColorConvertFloat4ToU32(c); }
inline IMGUI_API ImU32 GetColorU32(const ImVec4& col) { ImVec4 c = col; c.w *= GImGui->Style.Alpha; return ImGui::ColorConvertFloat4ToU32(c); }