From 9f92cc255bbc9a922211806e6ea50e4c5ab91cb7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 16 Jul 2016 10:36:09 +0200 Subject: [PATCH 001/319] SetActiveId() sets ActiveIdIsJustActivated only when id changes. (#323) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 19b44536..b8a6fe9c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1803,9 +1803,9 @@ ImGuiWindow* ImGui::GetParentWindow() void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window = NULL) { ImGuiContext& g = *GImGui; + g.ActiveIdIsJustActivated = (g.ActiveId != id); g.ActiveId = id; g.ActiveIdAllowOverlap = false; - g.ActiveIdIsJustActivated = true; g.ActiveIdWindow = window; } From bbd3b7560922fe3ff68cf418ed7ada7370a31c79 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 16 Jul 2016 10:46:22 +0200 Subject: [PATCH 002/319] Added IsKeyPressed() with explicit repeat delay and repeat rate (for usage by nav) (#323) --- imgui.cpp | 34 ++++++++++++++++++++++++---------- imgui.h | 1 + 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b8a6fe9c..a8f70f32 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -652,6 +652,7 @@ static void LogRenderedText(const ImVec2& ref_pos, const char* text, static void PushMultiItemsWidths(int components, float w_full = 0.0f); static float GetDraggedColumnOffset(int column_index); +static bool IsKeyDownMap(ImGuiKey key); static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true); static void SetCurrentFont(ImFont* font); @@ -3065,10 +3066,16 @@ bool ImGui::IsPosHoveringAnyWindow(const ImVec2& pos) return FindHoveredWindow(pos, false) != NULL; } +static bool IsKeyDownMap(ImGuiKey key) +{ + const int key_index = GImGui->IO.KeyMap[key]; + return (key_index >= 0) ? ImGui::IsKeyDown(key_index) : false; +} + static bool IsKeyPressedMap(ImGuiKey key, bool repeat) { const int key_index = GImGui->IO.KeyMap[key]; - return ImGui::IsKeyPressed(key_index, repeat); + return (key_index >= 0) ? ImGui::IsKeyPressed(key_index, repeat) : false; } int ImGui::GetKeyIndex(ImGuiKey key) @@ -3084,7 +3091,7 @@ bool ImGui::IsKeyDown(int key_index) return GImGui->IO.KeysDown[key_index]; } -bool ImGui::IsKeyPressed(int key_index, bool repeat) +bool ImGui::IsKeyPressed(int key_index, float repeat_delay, float repeat_rate) { ImGuiContext& g = *GImGui; if (key_index < 0) return false; @@ -3092,16 +3099,23 @@ bool ImGui::IsKeyPressed(int key_index, bool repeat) const float t = g.IO.KeysDownDuration[key_index]; if (t == 0.0f) return true; - - if (repeat && t > g.IO.KeyRepeatDelay) - { - float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate; - if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f)) + + if (t > repeat_delay && repeat_rate > 0.0f) + if ((fmodf(t - repeat_delay, repeat_rate) > repeat_rate*0.5f) != (fmodf(t - repeat_delay - g.IO.DeltaTime, repeat_rate) > repeat_rate*0.5f)) return true; - } + return false; } +bool ImGui::IsKeyPressed(int key_index, bool repeat) +{ + ImGuiContext& g = *GImGui; + if (repeat) + return IsKeyPressed(key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate); + else + return IsKeyPressed(key_index, 0.0f, 0.0f); +} + bool ImGui::IsKeyReleased(int key_index) { ImGuiContext& g = *GImGui; @@ -9725,8 +9739,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("FocusedWindow: '%s'", g.FocusedWindow ? g.FocusedWindow->Name : "NULL"); ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); - ImGui::Text("HoveredID: 0x%08X/0x%08X", g.HoveredId, g.HoveredIdPreviousFrame); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not - ImGui::Text("ActiveID: 0x%08X/0x%08X", g.ActiveId, g.ActiveIdPreviousFrame); + ImGui::Text("HoveredId: 0x%08X/0x%08X", g.HoveredId, g.HoveredIdPreviousFrame); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not + ImGui::Text("ActiveId: 0x%08X/0x%08X", g.ActiveId, g.ActiveIdPreviousFrame); ImGui::TreePop(); } } diff --git a/imgui.h b/imgui.h index dffb5060..c4b8c35f 100644 --- a/imgui.h +++ b/imgui.h @@ -430,6 +430,7 @@ namespace ImGui IMGUI_API int GetKeyIndex(ImGuiKey key); // map ImGuiKey_* values into user's key index. == io.KeyMap[key] IMGUI_API bool IsKeyDown(int key_index); // key_index into the keys_down[] array, imgui doesn't know the semantic of each entry, uses your own indices! IMGUI_API bool IsKeyPressed(int key_index, bool repeat = true); // uses user's key indices as stored in the keys_down[] array. if repeat=true. uses io.KeyRepeatDelay / KeyRepeatRate + IMGUI_API bool IsKeyPressed(int key_index, float repeat_delay, float repeat_rate); // uses user's key indices as stored in the keys_down[] array. uses provided repeat rate/delay IMGUI_API bool IsKeyReleased(int key_index); // " IMGUI_API bool IsMouseDown(int button); // is mouse button held IMGUI_API bool IsMouseClicked(int button, bool repeat = false); // did mouse button clicked (went from !Down to Down) From 91e1c56da614ac635fcc61b5a9e37e270c814f21 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 16 Jul 2016 10:46:51 +0200 Subject: [PATCH 003/319] Minor bits. --- imgui.cpp | 4 +--- imgui_demo.cpp | 2 +- imgui_internal.h | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a8f70f32..e2952a28 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4488,10 +4488,8 @@ void ImGui::FocusWindow(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - // Always mark the window we passed as focused. This is used for keyboard interactions such as tabbing. - g.FocusedWindow = window; - // Passing NULL allow to disable keyboard focus + g.FocusedWindow = window; if (!window) return; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 67846e7a..20337f38 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -784,7 +784,7 @@ void ImGui::ShowTestWindow(bool* p_open) struct Funcs { static float Sin(void*, int i) { return sinf(i * 0.1f); } - static float Saw(void*, int i) { return (i & 1) ? 1.0f : 0.0f; } + static float Saw(void*, int i) { return (i & 1) ? 1.0f : -1.0f; } }; static int func_type = 0, display_count = 70; ImGui::Separator(); diff --git a/imgui_internal.h b/imgui_internal.h index 4dfa6479..21a345bf 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -482,7 +482,7 @@ struct ImGuiContext DragCurrentValue = 0.0f; DragLastMouseDelta = ImVec2(0.0f, 0.0f); DragSpeedDefaultRatio = 1.0f / 100.0f; - DragSpeedScaleSlow = 0.01f; + DragSpeedScaleSlow = 1.0f / 100.0f; DragSpeedScaleFast = 10.0f; ScrollbarClickDeltaToGrabCenter = ImVec2(0.0f, 0.0f); memset(Tooltip, 0, sizeof(Tooltip)); From 6f7da2f9f288ac20e5e7fdcb6aa5fefdd9571059 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 16 Jul 2016 10:50:14 +0200 Subject: [PATCH 004/319] Popup: Fixed popup initial frame reading MousePos again instead of the value already stored within the CurrentPopupStack in the previous frame. Doesn't make a big difference here, but will do as gamepad/keyboard navigation will set its own popup position. (#323) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index e2952a28..b80a6999 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3937,7 +3937,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us // Popup first latch mouse position, will position itself when it appears next frame window->AutoPosLastDirection = -1; if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api) - window->PosFloat = g.IO.MousePos; + window->PosFloat = g.CurrentPopupStack.back().MousePosOnOpen; } // Collapse window by double-clicking on title bar From 57841f417d09c2c6b75bd9425c57173984f94db9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 16 Jul 2016 11:06:30 +0200 Subject: [PATCH 005/319] SliderBehavior, RoundScalar: split into separate functions for usage in upcoming nav commits. Testing power==1.0f without fabsf(). Maybe just use == 1.0f as well? (#323) --- imgui.cpp | 56 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b80a6999..a8e976c3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6360,13 +6360,18 @@ int ImGui::ParseFormatPrecision(const char* fmt, int default_precision) return precision; } +static float GetMinimumStepAtDecimalPrecision(int decimal_precision) +{ + static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; + return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision); +} + float ImGui::RoundScalar(float value, int decimal_precision) { // Round past decimal precision // So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0 // FIXME: Investigate better rounding methods - static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; - float min_step = (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision); + const float min_step = GetMinimumStepAtDecimalPrecision(decimal_precision); bool negative = value < 0.0f; value = fabsf(value); float remainder = fmodf(value, min_step); @@ -6377,6 +6382,28 @@ float ImGui::RoundScalar(float value, int decimal_precision) return negative ? -value : value; } +static inline float SliderBehaviorCalcRatioFromValue(float v, float v_min, float v_max, float power, float linear_zero_pos) +{ + const bool is_non_linear = (power < 1.0f-0.00001f) && (power > 1.0f-0.00001f); + if (is_non_linear) + { + float v_clamped = ImClamp(v, v_min, v_max); + if (v_clamped < 0.0f) + { + const float f = 1.0f - (v_clamped - v_min) / (ImMin(0.0f,v_max) - v_min); + return (1.0f - powf(f, 1.0f/power)) * linear_zero_pos; + } + else + { + const float f = (v_clamped - ImMax(0.0f,v_min)) / (v_max - ImMax(0.0f,v_min)); + return linear_zero_pos + powf(f, 1.0f/power) * (1.0f - linear_zero_pos); + } + } + + // Linear slider + return (ImClamp(v, v_min, v_max) - v_min) / (v_max - v_min); +} + bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags) { ImGuiContext& g = *GImGui; @@ -6386,7 +6413,7 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v // Draw frame RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - const bool is_non_linear = fabsf(power - 1.0f) > 0.0001f; + const bool is_non_linear = (power < 1.0f-0.00001f) && (power > 1.0f-0.00001f); const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0; const float grab_padding = 2.0f; @@ -6469,29 +6496,8 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v } } - // Calculate slider grab positioning - float grab_t; - if (is_non_linear) - { - float v_clamped = ImClamp(*v, v_min, v_max); - if (v_clamped < 0.0f) - { - const float f = 1.0f - (v_clamped - v_min) / (ImMin(0.0f,v_max) - v_min); - grab_t = (1.0f - powf(f, 1.0f/power)) * linear_zero_pos; - } - else - { - const float f = (v_clamped - ImMax(0.0f,v_min)) / (v_max - ImMax(0.0f,v_min)); - grab_t = linear_zero_pos + powf(f, 1.0f/power) * (1.0f - linear_zero_pos); - } - } - else - { - // Linear slider - grab_t = (ImClamp(*v, v_min, v_max) - v_min) / (v_max - v_min); - } - // Draw + float grab_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos); if (!is_horizontal) grab_t = 1.0f - grab_t; const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); From 848e62bfe0fe6083668afebdfaa5ab234a4817d9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 16 Jul 2016 11:22:49 +0200 Subject: [PATCH 006/319] InputText: don't process keys during the activation frame (this is mostly a workaround to that activation with enter doesn't validate immediately, which is turns triggers an assert in InputScalarAsWidgetReplacement - can't see an issue with changing it this way so trying out) + using local flag clear_active_id to only clear the active id at the end of the frame, which is one of the step that my stash for #701 requires. (#323) --- imgui.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a8e976c3..cd1261c6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7663,6 +7663,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 const bool user_clicked = hovered && io.MouseClicked[0]; const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY"); + bool clear_active_id = false; bool select_all = (g.ActiveId != id) && (flags & ImGuiInputTextFlags_AutoSelectAll) != 0; if (focus_requested || user_clicked || user_scrolled) { @@ -7708,8 +7709,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 else if (io.MouseClicked[0]) { // Release focus when we click outside - if (g.ActiveId == id) - SetActiveID(0); + clear_active_id = true; } bool value_changed = false; @@ -7781,9 +7781,12 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Consume characters memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); } + } - // Handle various key-presses - bool cancel_edit = false; + bool cancel_edit = false; + if (g.ActiveId == id && !g.ActiveIdIsJustActivated) + { + // Handle key-presses const int k_mask = (is_shift_down ? STB_TEXTEDIT_K_SHIFT : 0); const bool is_shortcutkey_only = (io.ShortcutsUseSuperKey ? (is_super_down && !is_alt_down && !is_shift_down && !is_ctrl_down) : (is_ctrl_down && !is_alt_down && !is_shift_down && !is_super_down)); const bool is_wordmove_key_down = (io.WordMovementUsesAltKey ? io.KeyAlt : io.KeyCtrl); @@ -7800,10 +7803,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 { bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; if (!is_multiline || (ctrl_enter_for_new_line && !is_ctrl_down) || (!ctrl_enter_for_new_line && is_ctrl_down)) - { - SetActiveID(0); - enter_pressed = true; - } + enter_pressed = clear_active_id = true; else if (is_editable) { unsigned int c = '\n'; // Insert new line @@ -7817,7 +7817,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (InputTextFilterCharacter(&c, flags, callback, user_data)) edit_state.OnKeyPressed((int)c); } - else if (IsKeyPressedMap(ImGuiKey_Escape)) { SetActiveID(0); cancel_edit = true; } + else if (IsKeyPressedMap(ImGuiKey_Escape)) { clear_active_id = cancel_edit = true; } else if (is_shortcutkey_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); } else if (is_shortcutkey_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); } else if (is_shortcutkey_only && IsKeyPressedMap(ImGuiKey_A)) { edit_state.SelectAll(); edit_state.CursorFollow = true; } @@ -7874,7 +7874,10 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } } } + } + if (g.ActiveId == id) + { if (cancel_edit) { // Restore initial value @@ -7972,6 +7975,10 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } } + // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) + if (clear_active_id && g.ActiveId == id) + SetActiveID(0); + // Render // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on. const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL; From 272745bd93c573ca774d44f1a03a27984becc0d7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 17 Jul 2016 10:26:48 +0200 Subject: [PATCH 007/319] ImGuiIO: initializing fields in the same order as the declaration. --- imgui.cpp | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cd1261c6..489c0c4e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -798,29 +798,32 @@ ImGuiIO::ImGuiIO() // Most fields are initialized with zero memset(this, 0, sizeof(*this)); + // Settings DisplaySize = ImVec2(-1.0f, -1.0f); DeltaTime = 1.0f/60.0f; IniSavingRate = 5.0f; IniFilename = "imgui.ini"; LogFilename = "imgui_log.txt"; - Fonts = &GImDefaultFontAtlas; - FontGlobalScale = 1.0f; - DisplayFramebufferScale = ImVec2(1.0f, 1.0f); - MousePos = ImVec2(-1,-1); - MousePosPrev = ImVec2(-1,-1); MouseDoubleClickTime = 0.30f; MouseDoubleClickMaxDist = 6.0f; - MouseDragThreshold = 6.0f; - for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) - MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; - for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) - KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f; for (int i = 0; i < ImGuiKey_COUNT; i++) KeyMap[i] = -1; KeyRepeatDelay = 0.250f; KeyRepeatRate = 0.050f; UserData = NULL; + Fonts = &GImDefaultFontAtlas; + FontGlobalScale = 1.0f; + FontAllowUserScaling = false; + DisplayFramebufferScale = ImVec2(1.0f, 1.0f); + DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f); +#ifdef __APPLE__ + WordMovementUsesAltKey = true; // OS X style: Text editing cursor movement using Alt instead of Ctrl + ShortcutsUseSuperKey = true; // OS X style: Shortcuts using Cmd/Super instead of Ctrl + DoubleClickSelectsWord = true; // OS X style: Double click selects by word instead of selecting whole text + MultiSelectUsesSuperKey = true; // OS X style: Multi-selection in lists uses Cmd/Super instead of Ctrl +#endif + // User functions RenderDrawListsFn = NULL; MemAllocFn = malloc; @@ -828,14 +831,16 @@ ImGuiIO::ImGuiIO() GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; + ImeWindowHandle = NULL; - // Set OS X style defaults based on __APPLE__ compile time flag -#ifdef __APPLE__ - WordMovementUsesAltKey = true; // OS X style: Text editing cursor movement using Alt instead of Ctrl - ShortcutsUseSuperKey = true; // OS X style: Shortcuts using Cmd/Super instead of Ctrl - DoubleClickSelectsWord = true; // OS X style: Double click selects by word instead of selecting whole text - MultiSelectUsesSuperKey = true; // OS X style: Multi-selection in lists uses Cmd/Super instead of Ctrl -#endif + // Input (NB: we already have memset zero the entire structure) + MousePos = ImVec2(-1,-1); + MousePosPrev = ImVec2(-1,-1); + MouseDragThreshold = 6.0f; + for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) + MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; + for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) + KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f; } // Pass in translated ASCII characters for text input. From 267e54cf065abc98532190a1899b2ded971d0394 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 17 Jul 2016 10:35:17 +0200 Subject: [PATCH 008/319] NewFrame(): update activeid data and keyboard before mouse. Should have no side-effects! (So that later we can update Nav between Keyboard and Mouse) (#323) --- imgui.cpp | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 489c0c4e..f4eb8d32 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2095,7 +2095,22 @@ void ImGui::NewFrame() g.RenderDrawData.CmdLists = NULL; g.RenderDrawData.CmdListsCount = g.RenderDrawData.TotalVtxCount = g.RenderDrawData.TotalIdxCount = 0; - // Update inputs state + // Clear reference to active widget if the widget isn't alive anymore + g.HoveredIdPreviousFrame = g.HoveredId; + g.HoveredId = 0; + g.HoveredIdAllowOverlap = false; + if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0) + SetActiveID(0); + g.ActiveIdPreviousFrame = g.ActiveId; + g.ActiveIdIsAlive = false; + g.ActiveIdIsJustActivated = false; + + // Update keyboard input state + memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); + for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) + g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f; + + // Update mouse input state if (g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0) g.IO.MousePos = ImVec2(-9999.0f, -9999.0f); if ((g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0) || (g.IO.MousePosPrev.x < 0 && g.IO.MousePosPrev.y < 0)) // if mouse just appeared or disappeared (negative coordinate) we cancel out movement in MouseDelta @@ -2130,9 +2145,6 @@ void ImGui::NewFrame() g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i])); } } - memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); - for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) - g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f; // Calculate frame-rate for the user, as a purely luxurious feature g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; @@ -2140,16 +2152,6 @@ void ImGui::NewFrame() g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame)); - // Clear reference to active widget if the widget isn't alive anymore - g.HoveredIdPreviousFrame = g.HoveredId; - g.HoveredId = 0; - g.HoveredIdAllowOverlap = false; - if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0) - SetActiveID(0); - g.ActiveIdPreviousFrame = g.ActiveId; - g.ActiveIdIsAlive = false; - g.ActiveIdIsJustActivated = false; - // Handle user moving window (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows. if (g.MovedWindowMoveId && g.MovedWindowMoveId == g.ActiveId) { @@ -3276,8 +3278,7 @@ bool ImGui::IsAnyItemActive() bool ImGui::IsItemVisible() { ImGuiWindow* window = GetCurrentWindowRead(); - ImRect r(window->ClipRect); - return r.Overlaps(window->DC.LastItemRect); + return window->ClipRect.Overlaps(window->DC.LastItemRect); } // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. From 068984691e11e1200ed5a908e17b6e1d7b99ee5f Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 17 Jul 2016 12:53:11 +0200 Subject: [PATCH 009/319] Tab-key focusing inhibited when CTRL is held (#323) --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f4eb8d32..1ccca0ac 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1923,7 +1923,7 @@ bool ImGui::FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_ // Process keyboard input at this point: TAB, Shift-TAB switch focus // We can always TAB out of a widget that doesn't allow tabbing in. - if (tab_stop && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && is_active && IsKeyPressedMap(ImGuiKey_Tab)) + if (tab_stop && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && is_active && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)) { // Modulo on index will be applied at the end of frame once we've got the total counter of items. window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1); @@ -2268,7 +2268,7 @@ void ImGui::NewFrame() // Pressing TAB activate widget focus // NB: Don't discard FocusedWindow if it isn't active, so that a window that go on/off programatically won't lose its keyboard focus. - if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Active && IsKeyPressedMap(ImGuiKey_Tab, false)) + if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Active && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false)) g.FocusedWindow->FocusIdxTabRequestNext = 0; // Mark all windows as not visible From 2df229d0021436fef3dee8f0cd32f5853c4b0985 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 17 Jul 2016 14:26:01 +0200 Subject: [PATCH 010/319] Fixed incorrect comment for ParentWindow (#615, #604) --- imgui_internal.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 21a345bf..2568aaaa 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -367,7 +367,7 @@ struct ImGuiContext ImGuiID HoveredIdPreviousFrame; ImGuiID ActiveId; // Active widget ImGuiID ActiveIdPreviousFrame; - bool ActiveIdIsAlive; + bool ActiveIdIsAlive; // Active widget has been seen this frame bool ActiveIdIsJustActivated; // Set at the time of activation for one frame bool ActiveIdAllowOverlap; // Set only by active widget ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) @@ -641,7 +641,7 @@ struct IMGUI_API ImGuiWindow ImDrawList* DrawList; ImGuiWindow* RootWindow; // If we are a child window, this is pointing to the first non-child parent window. Else point to ourself. ImGuiWindow* RootNonPopupWindow; // If we are a child window, this is pointing to the first non-child non-popup parent window. Else point to ourself. - ImGuiWindow* ParentWindow; // If we are a child window, this is pointing to our parent window. Else point to NULL. + ImGuiWindow* ParentWindow; // Immediate parent in the window stack *regardless* of whether this window is a child window or not) // Navigation / Focus int FocusIdxAllCounter; // Start at -1 and increase as assigned via FocusItemRegister() From 0300e73b3f07445e1e6bd154dedfad60f7aaf585 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 18 Jul 2016 13:03:47 +0200 Subject: [PATCH 011/319] Comments --- imgui.h | 12 ++++++------ imgui_internal.h | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/imgui.h b/imgui.h index c4b8c35f..19eae6a1 100644 --- a/imgui.h +++ b/imgui.h @@ -393,11 +393,11 @@ namespace ImGui IMGUI_API void PopClipRect(); // Utilities - IMGUI_API bool IsItemHovered(); // was the last item hovered by mouse? - IMGUI_API bool IsItemHoveredRect(); // was the last item hovered by mouse? even if another item is active or window is blocked by popup while we are hovering this - IMGUI_API bool IsItemActive(); // was the last item active? (e.g. button being held, text field being edited- items that don't interact will always return false) - IMGUI_API bool IsItemClicked(int mouse_button = 0); // was the last item clicked? (e.g. button/node just clicked on) - IMGUI_API bool IsItemVisible(); // was the last item visible? (aka not out of sight due to clipping/scrolling.) + IMGUI_API bool IsItemHovered(); // is the last item hovered by mouse? + IMGUI_API bool IsItemHoveredRect(); // is the last item hovered by mouse? even if another item is active or window is blocked by popup while we are hovering this + IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited- items that don't interact will always return false) + IMGUI_API bool IsItemClicked(int mouse_button = 0); // is the last item clicked? (e.g. button/node just clicked on) + IMGUI_API bool IsItemVisible(); // is the last item visible? (aka not out of sight due to clipping/scrolling.) IMGUI_API bool IsAnyItemHovered(); IMGUI_API bool IsAnyItemActive(); IMGUI_API ImVec2 GetItemRectMin(); // get bounding rect of last item in screen space @@ -812,7 +812,7 @@ struct ImGuiIO bool WantCaptureMouse; // Mouse is hovering a window or widget is active (= ImGui will use your mouse input) bool WantCaptureKeyboard; // Widget is active (= ImGui will use your keyboard input) - bool WantTextInput; // Some text input widget is active, which will read input characters from the InputCharacters array. + bool WantTextInput; // Text input widget is active, which will read input characters from the InputCharacters array. float Framerate; // Framerate estimation, in frame per second. Rolling average estimation based on IO.DeltaTime over 120 frames int MetricsAllocs; // Number of active memory allocations int MetricsRenderVertices; // Vertices output during last call to Render() diff --git a/imgui_internal.h b/imgui_internal.h index 2568aaaa..577993dc 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -347,8 +347,8 @@ struct ImGuiContext ImGuiIO IO; ImGuiStyle Style; ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() - float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize() - float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Size of characters. + float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. + float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height. ImVec2 FontTexUvWhitePixel; // (Shortcut) == Font->TexUvWhitePixel float Time; @@ -357,8 +357,8 @@ struct ImGuiContext int FrameCountRendered; ImVector Windows; ImVector WindowsSortBuffer; - ImGuiWindow* CurrentWindow; // Being drawn into ImVector CurrentWindowStack; + ImGuiWindow* CurrentWindow; // Being drawn into ImGuiWindow* FocusedWindow; // Will catch keyboard inputs ImGuiWindow* HoveredWindow; // Will catch mouse inputs ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) @@ -369,7 +369,7 @@ struct ImGuiContext ImGuiID ActiveIdPreviousFrame; bool ActiveIdIsAlive; // Active widget has been seen this frame bool ActiveIdIsJustActivated; // Set at the time of activation for one frame - bool ActiveIdAllowOverlap; // Set only by active widget + bool ActiveIdAllowOverlap; // Active widget allows another widget to steal active id (generally for overlapping widgets, but not always) ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) ImGuiWindow* ActiveIdWindow; ImGuiWindow* MovedWindow; // Track the child window we clicked on to move a window. From e72e3b2cbc1aebb7fc7fcbb20e1a264322f3e104 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 18 Jul 2016 14:31:15 +0200 Subject: [PATCH 012/319] Demo: tweaks. --- imgui_demo.cpp | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 20337f38..3862e628 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -67,11 +67,17 @@ static void ShowExampleAppCustomRendering(bool* p_open); static void ShowExampleAppMainMenuBar(); static void ShowExampleMenuFile(); -static void ShowHelpMarker(const char* desc) +static void ShowHelpMarker(const char* desc, float wrap_width = 450.0f) { ImGui::TextDisabled("(?)"); if (ImGui::IsItemHovered()) - ImGui::SetTooltip(desc); + { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(wrap_width); + ImGui::TextUnformatted(desc); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } } void ImGui::ShowUserGuide() @@ -850,7 +856,8 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::NextColumn(); char buf[32]; sprintf(buf, "%08x", i*5731); - ImGui::Button(buf, ImVec2(-1.0f, 0.0f)); + if (ImGui::Button(buf, ImVec2(-1.0f, 0.0f))) + printf("Pressed '%s'\n", buf); } ImGui::EndChild(); ImGui::PopStyleVar(); @@ -1299,13 +1306,15 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::OpenPopup("Stacked 1"); if (ImGui::BeginPopupModal("Stacked 1")) { - ImGui::Text("Hello from Stacked The First"); + ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDarkening] for darkening."); + static int item = 1; + ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); - if (ImGui::Button("Another one..")) + if (ImGui::Button("Add another modal..")) ImGui::OpenPopup("Stacked 2"); if (ImGui::BeginPopupModal("Stacked 2")) { - ImGui::Text("Hello from Stacked The Second"); + ImGui::Text("Hello from Stacked The Second!\nWe are piling a modal over another here,\nand also testing if the menu-bar works."); if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); @@ -1458,7 +1467,7 @@ void ImGui::ShowTestWindow(bool* p_open) } bool node_open = ImGui::TreeNode("Tree within single cell"); - ImGui::SameLine(); ShowHelpMarker("NB: Tree node must be poped before ending the cell.\nThere's no storage of state per-cell."); + ImGui::SameLine(); ShowHelpMarker("NB: Tree node must be poped before ending the cell. There's no storage of state per-cell."); if (node_open) { ImGui::Columns(2, "tree items"); @@ -1488,6 +1497,10 @@ void ImGui::ShowTestWindow(bool* p_open) if (ImGui::CollapsingHeader("Keyboard, Mouse & Focus")) { + ImGuiIO& io = ImGui::GetIO(); + ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); + ImGui::SameLine(); ShowHelpMarker("Request ImGui to render a mouse cursor for you in software. Note that a mouse cursor rendered via regular GPU rendering will feel more laggy than hardware cursor, but will be more in sync with your other visuals."); + if (ImGui::TreeNode("Tabbing")) { ImGui::Text("Use TAB/SHIFT+TAB to cycle through keyboard editable fields."); @@ -1541,11 +1554,11 @@ void ImGui::ShowTestWindow(bool* p_open) // Draw a line between the button and the mouse cursor ImDrawList* draw_list = ImGui::GetWindowDrawList(); draw_list->PushClipRectFullScreen(); - draw_list->AddLine(ImGui::CalcItemRectClosestPoint(ImGui::GetIO().MousePos, true, -2.0f), ImGui::GetIO().MousePos, ImColor(ImGui::GetStyle().Colors[ImGuiCol_Button]), 4.0f); + draw_list->AddLine(ImGui::CalcItemRectClosestPoint(io.MousePos, true, -2.0f), io.MousePos, ImColor(ImGui::GetStyle().Colors[ImGuiCol_Button]), 4.0f); draw_list->PopClipRect(); ImVec2 value_raw = ImGui::GetMouseDragDelta(0, 0.0f); ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(0); - ImVec2 mouse_delta = ImGui::GetIO().MouseDelta; + ImVec2 mouse_delta = io.MouseDelta; ImGui::SameLine(); ImGui::Text("Raw (%.1f, %.1f), WithLockThresold (%.1f, %.1f), MouseDelta (%.1f, %.1f)", value_raw.x, value_raw.y, value_with_lock_threshold.x, value_with_lock_threshold.y, mouse_delta.x, mouse_delta.y); } ImGui::TreePop(); @@ -1553,8 +1566,6 @@ void ImGui::ShowTestWindow(bool* p_open) if (ImGui::TreeNode("Keyboard & Mouse State")) { - ImGuiIO& io = ImGui::GetIO(); - ImGui::Text("MousePos: (%g, %g)", io.MousePos.x, io.MousePos.y); ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } @@ -1567,9 +1578,9 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::Text("Keys release:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyReleased(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } ImGui::Text("KeyMods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); - ImGui::Text("WantCaptureMouse: %s", io.WantCaptureMouse ? "true" : "false"); - ImGui::Text("WantCaptureKeyboard: %s", io.WantCaptureKeyboard ? "true" : "false"); - ImGui::Text("WantTextInput: %s", io.WantTextInput ? "true" : "false"); + ImGui::Text("WantCaptureMouse: %d", io.WantCaptureMouse); + ImGui::Text("WantCaptureKeyboard: %d", io.WantCaptureKeyboard); + ImGui::Text("WantTextInput: %d", io.WantTextInput); ImGui::Button("Hovering me sets the\nkeyboard capture flag"); if (ImGui::IsItemHovered()) @@ -1584,9 +1595,8 @@ void ImGui::ShowTestWindow(bool* p_open) if (ImGui::TreeNode("Mouse cursors")) { - ImGui::TextWrapped("Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. You can also set io.MouseDrawCursor to ask ImGui to render the cursor for you in software."); - ImGui::Checkbox("io.MouseDrawCursor", &ImGui::GetIO().MouseDrawCursor); ImGui::Text("Hover to see mouse cursors:"); + ImGui::SameLine(); ShowHelpMarker("Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, otherwise your backend needs to handle it."); for (int i = 0; i < ImGuiMouseCursor_Count_; i++) { char label[32]; From 24f79b91bd12a094aa6892fb8d3843ec58aeb736 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 18 Jul 2016 14:37:38 +0200 Subject: [PATCH 013/319] Style: minor tweak to default color theme to make currently focused window more prominent (#323, #707) --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1ccca0ac..db6708fb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -758,9 +758,9 @@ ImGuiStyle::ImGuiStyle() Colors[ImGuiCol_FrameBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.30f); // Background of checkbox, radio button, plot, slider, text input Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.90f, 0.80f, 0.80f, 0.40f); Colors[ImGuiCol_FrameBgActive] = ImVec4(0.90f, 0.65f, 0.65f, 0.45f); - Colors[ImGuiCol_TitleBg] = ImVec4(0.27f, 0.27f, 0.54f, 0.83f); + Colors[ImGuiCol_TitleBg] = ImVec4(0.24f, 0.24f, 0.50f, 0.83f); Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.40f, 0.40f, 0.80f, 0.20f); - Colors[ImGuiCol_TitleBgActive] = ImVec4(0.32f, 0.32f, 0.63f, 0.87f); + Colors[ImGuiCol_TitleBgActive] = ImVec4(0.33f, 0.33f, 0.65f, 0.87f); Colors[ImGuiCol_MenuBarBg] = ImVec4(0.40f, 0.40f, 0.55f, 0.80f); Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.20f, 0.25f, 0.30f, 0.60f); Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.40f, 0.40f, 0.80f, 0.30f); From e10ecfe28ae09abccb4fcbba08944104f08b207f Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 18 Jul 2016 15:52:08 +0200 Subject: [PATCH 014/319] BeginChild(): tweak to make the code easier to understand --- imgui.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index db6708fb..755e137a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3569,7 +3569,7 @@ bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button) bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiWindow* parent_window = GetCurrentWindow(); ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow; const ImVec2 content_avail = GetContentRegionAvail(); @@ -3591,12 +3591,13 @@ bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, flags |= extra_flags; char title[256]; - ImFormatString(title, IM_ARRAYSIZE(title), "%s.%s", window->Name, str_id); + ImFormatString(title, IM_ARRAYSIZE(title), "%s.%s", parent_window->Name, str_id); bool ret = ImGui::Begin(title, NULL, size, -1.0f, flags); + ImGuiWindow* child_window = GetCurrentWindow(); + if (!(parent_window->Flags & ImGuiWindowFlags_ShowBorders)) + child_window->Flags &= ~ImGuiWindowFlags_ShowBorders; - if (!(window->Flags & ImGuiWindowFlags_ShowBorders)) - GetCurrentWindow()->Flags &= ~ImGuiWindowFlags_ShowBorders; return ret; } From c816e6c74250b8946333682663dc9365e4b14e13 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 18 Jul 2016 16:55:20 +0200 Subject: [PATCH 015/319] Fixed SetScrollX() handling of center ratio (which actually wasn't exposed publicly). (#323, #246) --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 755e137a..e0cc87e7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4113,7 +4113,8 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us // Apply scrolling if (window->ScrollTarget.x < FLT_MAX) { - window->Scroll.x = window->ScrollTarget.x; + float center_ratio = window->ScrollTargetCenterRatio.x; + window->Scroll.x = window->ScrollTarget.x - (center_ratio * window->SizeFull.x); window->ScrollTarget.x = FLT_MAX; } if (window->ScrollTarget.y < FLT_MAX) From eb405ab375f4b10109a003964425bebbabf96e5d Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 18 Jul 2016 17:09:22 +0200 Subject: [PATCH 016/319] DragBehavior: Moving code around, in what should be a no-op, to simplify upcoming Nav diff (#323, #180) --- imgui.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e0cc87e7..097606c8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6769,32 +6769,33 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s g.DragLastMouseDelta = ImVec2(0.f, 0.f); } + if (v_speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX) + v_speed = (v_max - v_min) * g.DragSpeedDefaultRatio; + float v_cur = g.DragCurrentValue; const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f); - if (fabsf(mouse_drag_delta.x - g.DragLastMouseDelta.x) > 0.0f) - { - float speed = v_speed; - if (speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX) - speed = (v_max - v_min) * g.DragSpeedDefaultRatio; - if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f) - speed = speed * g.DragSpeedScaleFast; - if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f) - speed = speed * g.DragSpeedScaleSlow; + float adjust_delta = mouse_drag_delta.x - g.DragLastMouseDelta.x; + if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f) + adjust_delta *= g.DragSpeedScaleFast; + if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f) + adjust_delta *= g.DragSpeedScaleSlow; + adjust_delta *= v_speed; - float delta = (mouse_drag_delta.x - g.DragLastMouseDelta.x) * speed; + if (fabsf(adjust_delta) > 0.0f) + { if (fabsf(power - 1.0f) > 0.001f) { // Logarithmic curve on both side of 0.0 float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur; float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f; - float v1 = powf(v0_abs, 1.0f / power) + (delta * v0_sign); + float v1 = powf(v0_abs, 1.0f / power) + (adjust_delta * v0_sign); float v1_abs = v1 >= 0.0f ? v1 : -v1; float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f; // Crossed sign line v_cur = powf(v1_abs, power) * v0_sign * v1_sign; // Reapply sign } else { - v_cur += delta; + v_cur += adjust_delta; } g.DragLastMouseDelta.x = mouse_drag_delta.x; From 23a81027e895a23702eef804c4ba719bc29f83cf Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 18 Jul 2016 17:17:51 +0200 Subject: [PATCH 017/319] Comment on invalid comment regarding preserving keyboard focus of a closed window, due to commit 19d02becef94e8e0f1d432a8bd55cd783876583c. This is probably still what we want. (followup #727) --- imgui.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 097606c8..27735416 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1924,10 +1924,7 @@ bool ImGui::FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_ // Process keyboard input at this point: TAB, Shift-TAB switch focus // We can always TAB out of a widget that doesn't allow tabbing in. if (tab_stop && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && is_active && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)) - { - // Modulo on index will be applied at the end of frame once we've got the total counter of items. - window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1); - } + window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent) return true; @@ -2267,7 +2264,7 @@ void ImGui::NewFrame() } // Pressing TAB activate widget focus - // NB: Don't discard FocusedWindow if it isn't active, so that a window that go on/off programatically won't lose its keyboard focus. + //// NB: Don't discard FocusedWindow if it isn't active, so that a window that go on/off programatically won't lose its keyboard focus. // [2016/07/17] That comment was made invalid by 19d02becef94e8e0f1d432a8bd55cd783876583c if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Active && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false)) g.FocusedWindow->FocusIdxTabRequestNext = 0; From 88c1966629fff80f49bc6ffa0deb205f4856f3a3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 18 Jul 2016 18:24:23 +0200 Subject: [PATCH 018/319] Added IsAnyWindowFocused(). Renamed IsMouseHoveringAnyWindow() -> IsAnyWindowHovered(), IsPosHoveringAnyWindow() -> IsAnyWindowHoveredAtPos(), IsMouseHoveringWindow() -> IsWindowHoveredRect() for consistency. Kept inline rediection function. --- imgui.cpp | 28 +++++++++++++++++----------- imgui.h | 16 ++++++++++------ 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 27735416..8f679470 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -150,6 +150,9 @@ Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. Also read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2016/07/18 (1.50) - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete). + - renamed IsPosHoveringAnyWindow() to IsAnyWindowHoveredAtPos() for consistency. Kept inline redirection function (will obsolete). + - renamed IsMouseHoveringWindow() to IsWindowHoveredRect() for consistency. Kept inline redirection function (will obsolete). - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you. However if your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar. @@ -391,7 +394,7 @@ A: You can read the 'io.WantCaptureXXX' flags in the ImGuiIO structure. Preferably read them after calling ImGui::NewFrame() to avoid those flags lagging by one frame, but either should be fine. When 'io.WantCaptureMouse' or 'io.WantCaptureKeyboard' flags are set you may want to discard/hide the inputs from the rest of your application. When 'io.WantInputsCharacters' is set to may want to notify your OS to popup an on-screen keyboard, if available. - ImGui is tracking dragging and widget activity that may occur outside the boundary of a window, so 'io.WantCaptureMouse' is a more accurate and complete than testing for ImGui::IsMouseHoveringAnyWindow(). + ImGui is tracking dragging and widget activity that may occur outside the boundary of a window, so 'io.WantCaptureMouse' is a more accurate and complete than testing for ImGui::IsAnyWindowHovered(). (Advanced note: text input releases focus on Return 'KeyDown', so the following Return 'KeyUp' event that your application receive will typically have 'io.WantcaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs were for ImGui (e.g. with an array of bool) and filter out the corresponding key-ups.) @@ -1889,7 +1892,6 @@ bool ImGui::IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindowRead(); - if (!bb.Overlaps(window->ClipRect)) if (!id || *id != GImGui->ActiveId) if (clip_even_when_logged || !g.LogEnabled) @@ -3053,19 +3055,22 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c return rect_for_touch.Contains(g.IO.MousePos); } -bool ImGui::IsMouseHoveringWindow() +bool ImGui::IsWindowHoveredRect() { - ImGuiContext& g = *GImGui; - return g.HoveredWindow == g.CurrentWindow; + return GImGui->HoveredWindow == GImGui->CurrentWindow; } -bool ImGui::IsMouseHoveringAnyWindow() +bool ImGui::IsAnyWindowHovered() { - ImGuiContext& g = *GImGui; - return g.HoveredWindow != NULL; + return GImGui->HoveredWindow != NULL; } -bool ImGui::IsPosHoveringAnyWindow(const ImVec2& pos) +bool ImGui::IsAnyWindowFocused() +{ + return GImGui->FocusedWindow != NULL; +} + +bool ImGui::IsAnyWindowHoveredAtPos(const ImVec2& pos) { return FindHoveredWindow(pos, false) != NULL; } @@ -3550,7 +3555,7 @@ bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button) bool ImGui::BeginPopupContextWindow(bool also_over_items, const char* str_id, int mouse_button) { if (!str_id) str_id = "window_context_menu"; - if (IsMouseHoveringWindow() && IsMouseClicked(mouse_button)) + if (IsWindowHoveredRect() && IsMouseClicked(mouse_button)) if (also_over_items || !IsAnyItemHovered()) OpenPopupEx(str_id, true); return BeginPopup(str_id); @@ -3559,7 +3564,7 @@ bool ImGui::BeginPopupContextWindow(bool also_over_items, const char* str_id, in bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button) { if (!str_id) str_id = "void_context_menu"; - if (!IsMouseHoveringAnyWindow() && IsMouseClicked(mouse_button)) + if (!IsAnyWindowHovered() && IsMouseClicked(mouse_button)) OpenPopupEx(str_id, true); return BeginPopup(str_id); } @@ -5476,6 +5481,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool return false; } + // Default behavior requires click+release on same spot if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0) flags |= ImGuiButtonFlags_PressedOnClickRelease; diff --git a/imgui.h b/imgui.h index 19eae6a1..2db1a3ea 100644 --- a/imgui.h +++ b/imgui.h @@ -393,7 +393,7 @@ namespace ImGui IMGUI_API void PopClipRect(); // Utilities - IMGUI_API bool IsItemHovered(); // is the last item hovered by mouse? + IMGUI_API bool IsItemHovered(); // is the last item hovered by mouse, and usable? IMGUI_API bool IsItemHoveredRect(); // is the last item hovered by mouse? even if another item is active or window is blocked by popup while we are hovering this IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited- items that don't interact will always return false) IMGUI_API bool IsItemClicked(int mouse_button = 0); // is the last item clicked? (e.g. button/node just clicked on) @@ -404,13 +404,16 @@ namespace ImGui IMGUI_API ImVec2 GetItemRectMax(); // " IMGUI_API ImVec2 GetItemRectSize(); // " IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. - IMGUI_API bool IsWindowHovered(); // is current window hovered and hoverable (not blocked by a popup) (differentiate child windows from each others) IMGUI_API bool IsWindowFocused(); // is current window focused + IMGUI_API bool IsWindowHovered(); // is current window hovered and hoverable (not blocked by a popup) (differentiate child windows from each others) + IMGUI_API bool IsWindowHoveredRect(); // is current window hovered, disregarding of any consideration of being blocked by a popup. (unlike IsWindowHovered() this will return true even if the window is blocked because of a popup) IMGUI_API bool IsRootWindowFocused(); // is current root window focused (root = top-most parent of a child, otherwise self) IMGUI_API bool IsRootWindowOrAnyChildFocused(); // is current root window or any of its child (including current window) focused IMGUI_API bool IsRootWindowOrAnyChildHovered(); // is current root window or any of its child (including current window) hovered and hoverable (not blocked by a popup) + IMGUI_API bool IsAnyWindowFocused(); + IMGUI_API bool IsAnyWindowHovered(); // is mouse hovering any visible window + IMGUI_API bool IsAnyWindowHoveredAtPos(const ImVec2& pos); // is given position hovering any active imgui window IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle of given size starting from cursor pos is visible (not clipped). to perform coarse clipping on user's side (as an optimization) - IMGUI_API bool IsPosHoveringAnyWindow(const ImVec2& pos); // is given position hovering any active imgui window IMGUI_API float GetTime(); IMGUI_API int GetFrameCount(); IMGUI_API const char* GetStyleColName(ImGuiCol idx); @@ -436,10 +439,8 @@ namespace ImGui IMGUI_API bool IsMouseClicked(int button, bool repeat = false); // did mouse button clicked (went from !Down to Down) IMGUI_API bool IsMouseDoubleClicked(int button); // did mouse button double-clicked. a double-click returns false in IsMouseClicked(). uses io.MouseDoubleClickTime. IMGUI_API bool IsMouseReleased(int button); // did mouse button released (went from Down to !Down) - IMGUI_API bool IsMouseHoveringWindow(); // is mouse hovering current window ("window" in API names always refer to current window). disregarding of any consideration of being blocked by a popup. (unlike IsWindowHovered() this will return true even if the window is blocked because of a popup) - IMGUI_API bool IsMouseHoveringAnyWindow(); // is mouse hovering any visible window - IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true); // is mouse hovering given bounding rect (in screen space). clipped by current clipping settings. disregarding of consideration of focus/window ordering/blocked by a popup. IMGUI_API bool IsMouseDragging(int button = 0, float lock_threshold = -1.0f); // is mouse dragging. if lock_threshold < -1.0f uses io.MouseDraggingThreshold + IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true); // is mouse hovering given bounding rect (in screen space). clipped by current clipping settings. disregarding of consideration of focus/window ordering/blocked by a popup. IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve backup of mouse positioning at the time of opening popup we have BeginPopup() into IMGUI_API ImVec2 GetMouseDragDelta(int button = 0, float lock_threshold = -1.0f); // dragging amount since clicking. if lock_threshold < -1.0f uses io.MouseDraggingThreshold @@ -465,6 +466,9 @@ namespace ImGui // Obsolete (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + static inline bool IsPosHoveringAnyWindow(const ImVec2& pos) { return IsAnyWindowHoveredAtPos(pos); } // OBSOLETE 1.50+ + static inline bool IsMouseHoveringAnyWindow() { return IsAnyWindowHovered(); } // OBSOLETE 1.50+ + static inline bool IsMouseHoveringWindow() { return IsWindowHoveredRect(); } // OBSOLETE 1.50+ static inline bool CollapsingHeader(const char* label, const char* str_id, bool framed = true, bool default_open = false) { (void)str_id; (void)framed; ImGuiTreeNodeFlags default_open_flags = 1<<5; return CollapsingHeader(label, (default_open ? default_open_flags : 0)); } // OBSOLETE 1.49+ static inline ImFont* GetWindowFont() { return GetFont(); } // OBSOLETE 1.48+ static inline float GetWindowFontSize() { return GetFontSize(); } // OBSOLETE 1.48+ From 4a11cc35b901e7e49740cc60b5291dce72a5e9a5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 19 Jul 2016 21:26:36 +0200 Subject: [PATCH 019/319] Updated code for repeat delay / repeat handling. GetKeyPressedAmount() now returns a count to support fast repeat rate (where DeltaTime > RepeatRate). Renamed from recently added IsKeyPressed() variant to GetKeyPressedAmount(). (no API breakage, added in branch, bbd3b7560922fe3ff68cf418ed7ada7370a31c79) (#323) --- imgui.cpp | 18 +++++++++--------- imgui.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8f679470..bba5c176 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3100,29 +3100,29 @@ bool ImGui::IsKeyDown(int key_index) return GImGui->IO.KeysDown[key_index]; } -bool ImGui::IsKeyPressed(int key_index, float repeat_delay, float repeat_rate) +int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate) { ImGuiContext& g = *GImGui; if (key_index < 0) return false; IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); const float t = g.IO.KeysDownDuration[key_index]; if (t == 0.0f) - return true; - + return 1; if (t > repeat_delay && repeat_rate > 0.0f) - if ((fmodf(t - repeat_delay, repeat_rate) > repeat_rate*0.5f) != (fmodf(t - repeat_delay - g.IO.DeltaTime, repeat_rate) > repeat_rate*0.5f)) - return true; - - return false; + { + int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t - repeat_delay - g.IO.DeltaTime) / repeat_rate); + return (count > 0) ? count : 0; + } + return 0; } bool ImGui::IsKeyPressed(int key_index, bool repeat) { ImGuiContext& g = *GImGui; if (repeat) - return IsKeyPressed(key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate); + return GetKeyPressedAmount(key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; else - return IsKeyPressed(key_index, 0.0f, 0.0f); + return GetKeyPressedAmount(key_index, 0.0f, 0.0f) > 0; } bool ImGui::IsKeyReleased(int key_index) diff --git a/imgui.h b/imgui.h index 2db1a3ea..994b663c 100644 --- a/imgui.h +++ b/imgui.h @@ -433,8 +433,8 @@ namespace ImGui IMGUI_API int GetKeyIndex(ImGuiKey key); // map ImGuiKey_* values into user's key index. == io.KeyMap[key] IMGUI_API bool IsKeyDown(int key_index); // key_index into the keys_down[] array, imgui doesn't know the semantic of each entry, uses your own indices! IMGUI_API bool IsKeyPressed(int key_index, bool repeat = true); // uses user's key indices as stored in the keys_down[] array. if repeat=true. uses io.KeyRepeatDelay / KeyRepeatRate - IMGUI_API bool IsKeyPressed(int key_index, float repeat_delay, float repeat_rate); // uses user's key indices as stored in the keys_down[] array. uses provided repeat rate/delay IMGUI_API bool IsKeyReleased(int key_index); // " + IMGUI_API int GetKeyPressedAmount(int key_index, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, typically 0 or 1 but may be >1 if RepeatRate is small enough that DeltaTime > RepeatRate IMGUI_API bool IsMouseDown(int button); // is mouse button held IMGUI_API bool IsMouseClicked(int button, bool repeat = false); // did mouse button clicked (went from !Down to Down) IMGUI_API bool IsMouseDoubleClicked(int button); // did mouse button double-clicked. a double-click returns false in IsMouseClicked(). uses io.MouseDoubleClickTime. From c2cb2a6928a436b1358f5e960844c02cbab0b3ba Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 20 Jul 2016 00:02:59 +0200 Subject: [PATCH 020/319] Gamepad/Keyboard navigation support, initial commit, WIP (#323) --- imgui.cpp | 877 +++++++++++++++++++++++++++++++++++++++++------ imgui.h | 23 +- imgui_demo.cpp | 13 +- imgui_internal.h | 82 ++++- 4 files changed, 878 insertions(+), 117 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bba5c176..ad2007bd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,6 +1,9 @@ // dear imgui, v1.50 WIP // (main code and documentation) +// ** EXPERIMENTAL GAMEPAD/KEYBOARD NAVIGATION BRANCH +// ** Grep for FIXME-NAVIGATION + // See ImGui::ShowTestWindow() in imgui_demo.cpp for demo code. // Newcomers, read 'Programmer guide' below for notes on how to setup ImGui in your codebase. // Get latest version at https://github.com/ocornut/imgui @@ -616,7 +619,6 @@ #include // toupper, isprint #include // NULL, malloc, free, qsort, atoi #include // vsnprintf, sscanf, printf -#include // INT_MIN, INT_MAX #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier #include // intptr_t #else @@ -813,6 +815,7 @@ ImGuiIO::ImGuiIO() KeyMap[i] = -1; KeyRepeatDelay = 0.250f; KeyRepeatRate = 0.050f; + NavMovesMouse = false; UserData = NULL; Fonts = &GImDefaultFontAtlas; @@ -1736,6 +1739,7 @@ ImGuiWindow::ImGuiWindow(const char* name) SkipItems = false; BeginCount = 0; PopupId = 0; + NavLastId = 0; AutoFitFramesX = AutoFitFramesY = -1; AutoFitOnlyGrows = false; AutoPosLastDirection = -1; @@ -1790,6 +1794,13 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) return ImHash(str, str_end ? (int)(str_end - str) : 0, seed); } +ImGuiID ImGuiWindow::GetChildID(ImGuiWindow* child_window) +{ + IM_ASSERT(child_window && child_window->ParentWindow == this); + ImGuiID seed = IDStack[0]; + return ImHash(&child_window->ID, sizeof(child_window->ID), seed); +} + //----------------------------------------------------------------------------- // Internal API exposed in imgui_internal.h //----------------------------------------------------------------------------- @@ -1814,8 +1825,31 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window = NULL) ImGuiContext& g = *GImGui; g.ActiveIdIsJustActivated = (g.ActiveId != id); g.ActiveId = id; + g.ActiveIdAllowNavMove = false; g.ActiveIdAllowOverlap = false; g.ActiveIdWindow = window; + if (id) + { + g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavTabbedId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; + if (g.ActiveIdSource == ImGuiInputSource_Nav) + g.NavDisableMouseHover = true; + else + g.NavDisableHighlight = true; + g.NavId = id; + if (window) + window->NavLastId = id; + } +} + +void ImGui::SetActiveIDNoNav(ImGuiID id, ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + g.ActiveIdIsJustActivated = (g.ActiveId != id); + g.ActiveId = id; + g.ActiveIdAllowNavMove = false; + g.ActiveIdAllowOverlap = false; + g.ActiveIdWindow = window; + g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; } void ImGui::SetHoveredID(ImGuiID id) @@ -1860,20 +1894,188 @@ void ImGui::ItemSize(const ImRect& bb, float text_offset_y) ItemSize(bb.GetSize(), text_offset_y); } +static ImGuiNavDir NavScoreItemGetQuadrant(float dx, float dy) +{ + if (fabsf(dx) > fabsf(dy)) + return (dx > 0.0f) ? ImGuiNavDir_E : ImGuiNavDir_W; + return (dy > 0.0f) ? ImGuiNavDir_S : ImGuiNavDir_N; +} + +static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1) +{ + if (a1 < b0) return a1 - b0; + if (b1 < a0) return a0 - b1; + return 0.0f; +} + +// Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057 +// FIXME-NAVIGATION: Pretty rough. +// FIXME-NAVIGATION: May want to handle the degenerate case that we have commented out. +static bool NavScoreItem(const ImRect& cand) +{ + ImGuiContext& g = *GImGui; + const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having lots of items with varied width) + + // Compute distance between boxes + // FIXME-NAVIGATION: Introducing various biases toward typical imgui uses cases, but we don't have any rigorous proof of their effect now. + float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); + float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Clamp down on Y to keep using box-distance for vertically touching items + //dbx /= 2; dby *= 4 // Bias for dy + if (dby) + dbx = (dbx > 0.0f) ? +0.0f : -0.0f; + float dist_box = fabsf(dbx) + fabsf(dby); + + // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter) + float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x); + float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y); + float dist_center = fabsf(dcx) + fabsf(dcy); // L1 metric (need this for our connectedness guarantee) + + // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance + ImGuiNavDir quadrant; + float dax = 0.0f, day = 0.0f, dist_axial = 0.0f; + if (dbx || dby) + { + // For non-overlapping boxes, use distance between boxes + dax = dbx; + day = dby; + dist_axial = dist_box; + quadrant = NavScoreItemGetQuadrant(dbx, dby); + } + else if (dcx || dcy) + { + // For overlapping boxes with different centers, use distance between centers + dax = dcx; + day = dcy; + dist_axial = dist_center; + quadrant = NavScoreItemGetQuadrant(dcx, dcy); + } + else + { + // Degenerate case: two overlapping buttons with same center, break ties using order + quadrant = (g.CurrentWindow->DC.LastItemId < g.NavId) ? ImGuiNavDir_W : ImGuiNavDir_E; + } + +#if 0 // [DEBUG] + //draw_list->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,200)); + if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) + { + char buf[128]; + ImFormatString(buf, IM_ARRAYSIZE(buf), "db (%.0f,%.0f->%.1f) dc (%.0f,%.0f->%.1f) da (%.0f,%.0f->%.1f) quad %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[quadrant]); + g.OverlayDrawList.AddText(cand.Max, ~0U, buf); + } + #endif + + // Is it in the quadrant we're interesting in moving to? + bool new_best = false; + if (quadrant == g.NavMoveDir) + { + // Does it beat the current best candidate? + if (dist_box < g.NavMoveResultBestDistBox) + { + g.NavMoveResultBestDistBox = dist_box; + g.NavMoveResultBestDistCenter = dist_center; + return true; + } + if (dist_box == g.NavMoveResultBestDistBox) + { + // Try using distance between center points to break ties + if (dist_center < g.NavMoveResultBestDistCenter) + { + g.NavMoveResultBestDistCenter = dist_center; + new_best = true; + } + else if (dist_center == g.NavMoveResultBestDistCenter) + { + // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" buttons + // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), + // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis. + if ((g.NavMoveDir >= ImGuiNavDir_N ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance + new_best = true; + } + } + } + + // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches + // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness) + // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too. + if (g.NavMoveResultBestDistBox == FLT_MAX) + if (dist_axial < g.NavMoveResultBestDistAxial) // Check axial match + if ((g.NavMoveDir == ImGuiNavDir_W && dax < 0.0f) || (g.NavMoveDir == ImGuiNavDir_E && dax > 0.0f) || (g.NavMoveDir == ImGuiNavDir_N && day < 0.0f) || (g.NavMoveDir == ImGuiNavDir_S && day > 0.0f)) + g.NavMoveResultBestDistAxial = dist_axial, new_best = true; + + return new_best; +} + +static void RenderNavHighlight(ImU32 id, const ImRect& bb) +{ + ImGuiContext& g = *GImGui; + if (id != g.NavId || g.NavDisableHighlight) + return; + ImGuiWindow* window = ImGui::GetCurrentWindow(); + window->DrawList->PushClipRect(window->WindowRectClipped.Min - ImVec2(2,2), window->WindowRectClipped.Max + ImVec2(2,2)); + window->DrawList->AddRectFilled(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2), ImGui::GetColorU32(ImGuiCol_HeaderHovered, 0.15f), g.Style.FrameRounding); + window->DrawList->AddRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2), ImGui::GetColorU32(ImGuiCol_HeaderHovered), g.Style.FrameRounding); + //window->DrawList->AddRect(g.NavRefRectScreen.Min, g.NavRefRectScreen.Max, IM_COL32(255,0,0,255)); + window->DrawList->PopClipRect(); +} + // Declare item bounding box for clipping and interaction. // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface // declares their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd(). -bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id) +bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_arg) { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; window->DC.LastItemId = id ? *id : 0; window->DC.LastItemRect = bb; window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false; - if (IsClippedEx(bb, id, false)) + if (id != NULL) window->DC.NavHasItemsNext = true; + const bool is_clipped = IsClippedEx(bb, id, false); + + // Navigation processing runs prior to clipping early-out + // (a) So that NavInitDefaultRequest can be honored, for newly opened windows to select a default widget + // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests unfortunately, but it is still limited to one window. + // it may not scale very well for windows with ten of thousands of item, but at least the NavRequest is only performed on user interaction, aka maximum once a frame. + // We could early out with `if (is_clipped && !g.NavInitDefaultRequest) return false;` but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) + // A more pragmatic solution for handling last lists is relying on the fact that they are likely evenly spread items (so that clipper can work) and we could nav at higher-level (apply index, etc.) + // So eventually we would like to provide the user will the primitives to be able to implement that sort of customized/efficient navigation handling whenever necessary. + // FIXME-NAVIGATION + if (id != NULL && g.NavWindow == window && g.IO.NavUsable) + { + if (g.NavInitDefaultRequest && window->DC.AllowNavDefaultFocus) + { + g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = false; // Clear flag immediately, first item gets default, also simplify the if() in ItemAdd() + g.NavInitDefaultResultId = *id; + } + + if (g.NavMoveRequest && g.NavId != *id) + { + //if (!g.NavMoveRequest) g.NavMoveDir = ImGuiNavDir_E; // [DEBUG] Removing if (g.NavMoveRequest) above allows debug scoring of all visible items. + const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; + if (NavScoreItem(nav_bb)) + { + g.NavMoveResultBestId = *id; + g.NavMoveResultBestRefRectRel = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); + //g.OverlayDrawList.AddRectFilled(g.NavRefRectScreen.Min, g.NavRefRectScreen.Max+ImVec2(2,2), IM_COL32(255,255,0,255)); // [DEBUG] + //g.OverlayDrawList.AddRectFilled(nav_bb.Min, nav_bb.Max, IM_COL32(255,0,255,100)); // [DEBUG] + //g.OverlayDrawList.AddText(nav_bb.Min, ~0U, "new_best"); // [DEBUG] + } + } + + // Update window-relative bounding box of navigated item + if (g.NavId == *id) + { + const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; + g.NavRefRectRel = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); + g.NavIdIsAlive = true; + g.NavIdTabCounter = window->FocusIdxTabCounter; + } + } + + if (is_clipped) return false; - // This is a sensible default, but widgets are free to override it after calling ItemAdd() - ImGuiContext& g = *GImGui; + // Setting LastItemHoveredAndUsable for IsItemHovered() if (IsMouseHoveringRect(bb.Min, bb.Max)) { // Matching the behavior of IsHovered() but allow if ActiveId==window->MoveID (we clicked on the window background) @@ -1881,7 +2083,7 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id) window->DC.LastItemHoveredRect = true; if (g.HoveredRootWindow == window->RootWindow) if (g.ActiveId == 0 || (id && g.ActiveId == *id) || g.ActiveIdAllowOverlap || (g.ActiveId == window->MoveId)) - if (IsWindowContentHoverable(window)) + if (!g.NavDisableMouseHover && IsWindowContentHoverable(window)) window->DC.LastItemHoveredAndUsable = true; } @@ -1908,13 +2110,13 @@ bool ImGui::IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs) ImGuiWindow* window = GetCurrentWindowRead(); if (g.HoveredWindow == window || (flatten_childs && g.HoveredRootWindow == window->RootWindow)) if ((g.ActiveId == 0 || g.ActiveId == id || g.ActiveIdAllowOverlap) && IsMouseHoveringRect(bb.Min, bb.Max)) - if (IsWindowContentHoverable(g.HoveredRootWindow)) + if (!g.NavDisableMouseHover && IsWindowContentHoverable(g.HoveredRootWindow)) return true; } return false; } -bool ImGui::FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop) +bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop) { ImGuiContext& g = *GImGui; @@ -1923,17 +2125,21 @@ bool ImGui::FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_ if (allow_keyboard_focus) window->FocusIdxTabCounter++; - // Process keyboard input at this point: TAB, Shift-TAB switch focus - // We can always TAB out of a widget that doesn't allow tabbing in. - if (tab_stop && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && is_active && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)) + // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item. + // Note that we can always TAB out of a widget that doesn't allow tabbing in. + if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)) window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent) + { + g.NavTabbedId = id; return true; - - if (allow_keyboard_focus) - if (window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent) - return true; + } + if (allow_keyboard_focus && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent) + { + g.NavTabbedId = id; + return true; + } return false; } @@ -2058,6 +2264,191 @@ int ImGui::GetFrameCount() return GImGui->FrameCount; } +static void NavUpdate() +{ + ImGuiContext& g = *GImGui; + g.IO.WantMoveMouse = false; + + if (g.NavMousePosDirty && g.NavIdIsAlive) + { + // Set mouse position given our knowledge of the nav widget position from last frame + if (g.IO.NavMovesMouse) + { + g.IO.MousePos = g.IO.MousePosPrev = g.NavWindow->Pos + ImVec2(g.NavRefRectRel.Min.x + ImMin(g.Style.FramePadding.x*4, g.NavRefRectRel.GetWidth()), g.NavRefRectRel.Max.y - ImMin(g.Style.FramePadding.y, g.NavRefRectRel.GetHeight())); + g.IO.WantMoveMouse = true; + } + g.NavMousePosDirty = false; + } + g.NavIdIsAlive = false; + g.NavTabbedId = 0; + + if (g.NavInitDefaultResultId != 0 && (!g.NavDisableHighlight || g.NavInitDefaultResultExplicit)) + { + // Apply result from previous navigation init request (typically select the first item, unless SetItemDefaultFocus() has been called) + IM_ASSERT(g.NavWindow); + g.NavId = g.NavWindow->NavLastId = g.NavInitDefaultResultId; + //if (g.NavInitDefaultResultExplicit) + // g.NavDisableHighlight = false; + } + + if (g.NavMoveRequest) + { + // 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); + //g.OverlayDrawList.AddRect(g.NavWindow->Pos + window_rect_rel.Min, g.NavWindow->Pos + window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG] + if (g.NavWindow && g.NavMoveResultBestId != 0 && !window_rect_rel.Contains(g.NavMoveResultBestRefRectRel)) + { + if (g.NavWindow->ScrollbarX && g.NavMoveResultBestRefRectRel.Min.x < window_rect_rel.Min.x) + { + g.NavWindow->ScrollTarget.x = g.NavMoveResultBestRefRectRel.Min.x + g.NavWindow->Scroll.x - g.Style.ItemSpacing.x; + g.NavWindow->ScrollTargetCenterRatio.x = 0.0f; + } + else if (g.NavWindow->ScrollbarX && g.NavMoveResultBestRefRectRel.Max.x >= window_rect_rel.Max.x) + { + g.NavWindow->ScrollTarget.x = g.NavMoveResultBestRefRectRel.Max.x + g.NavWindow->Scroll.x + g.Style.ItemSpacing.x; + g.NavWindow->ScrollTargetCenterRatio.x = 1.0f; + } + if (g.NavMoveResultBestRefRectRel.Min.y < window_rect_rel.Min.y) + { + g.NavWindow->ScrollTarget.y = g.NavMoveResultBestRefRectRel.Min.y + g.NavWindow->Scroll.y - g.Style.ItemSpacing.y; + g.NavWindow->ScrollTargetCenterRatio.y = 0.0f; + } + else if (g.NavMoveResultBestRefRectRel.Max.y >= window_rect_rel.Max.y) + { + g.NavWindow->ScrollTarget.y = g.NavMoveResultBestRefRectRel.Max.y + g.NavWindow->Scroll.y + g.Style.ItemSpacing.y; + g.NavWindow->ScrollTargetCenterRatio.y = 1.0f; + } + } + } + + if (g.NavMoveRequest && g.NavMoveResultBestId != 0) + { + // Apply result from previous navigation directional move request + IM_ASSERT(g.NavWindow); + g.NavId = g.NavWindow->NavLastId = g.NavMoveResultBestId; + g.NavRefRectRel = g.NavMoveResultBestRefRectRel; + g.NavMousePosDirty = true; + g.NavDisableHighlight = false; + g.NavDisableMouseHover = true; + } + + // Navigation windowing mode (change focus, move/resize window) + if (!g.NavWindowingTarget && g.NavWindow && IsKeyPressedMap(ImGuiKey_NavWindowing, false)) + g.NavWindowingTarget = g.NavWindow->RootWindow; + if (g.NavWindowingTarget) + { + // FIXME-NAVIGATION: Need to clarify input semantic, naming is misleading/incorrect here. + int focus_change_dir = IsKeyPressedMap(ImGuiKey_NavTweakFaster, true) ? -1 : IsKeyPressedMap(ImGuiKey_NavTweakSlower, true) ? +1 : 0; + if (focus_change_dir != 0) + { + // FIXME-NAVIGATION FIXME-OPT: This is absolutely hideous and shouldn't stay. Pressed we should maintain a intrusive linked-list of visible windows. + int i_current = -1; + for (int i = g.Windows.Size-1; i >= 0 && i_current == -1; i--) + if (g.Windows[i] == g.NavWindowingTarget) + i_current = i; + int i_target = -1; + for (int i = i_current+focus_change_dir; i >= 0 && i < g.Windows.Size && i_target == -1; i += focus_change_dir) + if (g.Windows[i]->Active && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow)) + i_target = i; + for (int i = (focus_change_dir < 0) ? (g.Windows.Size-1) : 0; i >= 0 && i < g.Windows.Size && i_target == -1 && i_target != i_current; i += focus_change_dir) + if (g.Windows[i]->Active && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow)) + i_target = i; + if (i_target != -1) + { + IM_ASSERT(i_target != i_current); + g.NavWindowingTarget = g.Windows[i_target]; + } + } + + // End window select/focus mode and apply final focus + if (!IsKeyDownMap(ImGuiKey_NavWindowing)) + { + if (g.NavWindowingTarget) + if (!g.FocusedWindow || (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow != g.FocusedWindow->RootWindow)) + ImGui::FocusWindow(g.NavWindowingTarget->RootWindow); + g.NavWindowingTarget = NULL; + } + } + + // Set output flags for user application + g.IO.NavUsable = g.FocusedWindow && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNav); + g.IO.NavActive = g.IO.NavUsable && g.NavId != 0 && !g.NavDisableHighlight; + + // Process NavCancel input (to close a popup, get back to parent, clear focus) + if (g.ActiveId == 0 && IsKeyPressedMap(ImGuiKey_NavCancel)) + { + // Close open popup or move back to parent window + if (g.OpenPopupStack.Size > 0) + { + ClosePopupToLevel(g.OpenPopupStack.Size - 1); + } + else + { + // Clear NavId for popups but keep it for regular child window so we can leave one and come back where we were + if (g.FocusedWindow && ((g.FocusedWindow->Flags & ImGuiWindowFlags_Popup) || !(g.FocusedWindow->Flags & ImGuiWindowFlags_ChildWindow))) + g.FocusedWindow->NavLastId = 0; + + if (g.FocusedWindow && (g.FocusedWindow->Flags & ImGuiWindowFlags_ChildWindow) && g.FocusedWindow->ParentWindow) + { + // Exit child window + ImGuiWindow* child_window = g.FocusedWindow; + ImGuiWindow* parent_window = g.FocusedWindow->ParentWindow; + ImGui::FocusWindow(parent_window); + g.NavId = parent_window->NavLastId = parent_window->GetChildID(child_window); + g.NavIdIsAlive = false; + if (g.NavDisableMouseHover) + g.NavMousePosDirty = true; + } + else + { + g.NavId = 0; + } + } + } + + g.NavActivateId = (g.NavId && !g.NavDisableHighlight && g.ActiveId == 0 && IsKeyPressedMap(ImGuiKey_NavActivate)) ? g.NavId : 0; + g.NavInputId = (g.NavId && !g.NavDisableHighlight && g.ActiveId == 0 && IsKeyPressedMap(ImGuiKey_NavInput)) ? g.NavId : 0; + if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNav)) + { + g.NavActivateId = g.NavInputId = 0; + g.NavDisableHighlight = true; + } + g.NavMoveRequest = false; + g.NavInitDefaultRequest = false; + g.NavInitDefaultResultExplicit = false; + g.NavInitDefaultResultId = 0; + + // Initiate directional inputs request + g.NavMoveDir = ImGuiNavDir_None; + if (g.FocusedWindow && !g.NavWindowingTarget && (g.ActiveId == 0 || g.ActiveIdAllowNavMove) && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNav)) + { + if (IsKeyPressedMap(ImGuiKey_NavLeft, true)) g.NavMoveDir = ImGuiNavDir_W; + if (IsKeyPressedMap(ImGuiKey_NavRight, true)) g.NavMoveDir = ImGuiNavDir_E; + if (IsKeyPressedMap(ImGuiKey_NavUp, true)) g.NavMoveDir = ImGuiNavDir_N; + if (IsKeyPressedMap(ImGuiKey_NavDown, true)) g.NavMoveDir = ImGuiNavDir_S; + } + if (g.NavMoveDir != ImGuiNavDir_None) + { + g.NavMoveRequest = true; + g.NavWindow = g.FocusedWindow; + } + + // Fallback manual-scroll with NavUp/NavDown when window has no navigable item + if (g.FocusedWindow && !g.FocusedWindow->DC.NavHasItems && g.FocusedWindow->DC.NavHasScroll && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNav) && g.NavMoveRequest && (g.NavMoveDir == ImGuiNavDir_N || g.NavMoveDir == ImGuiNavDir_S)) + { + 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. + g.FocusedWindow->Scroll.y = ImFloor(g.FocusedWindow->Scroll.y + ((g.NavMoveDir == ImGuiNavDir_N) ? -1.0f : +1.0f) * scroll_speed); + } + + // Reset search + g.NavMoveResultBestId = 0; + g.NavMoveResultBestDistAxial = g.NavMoveResultBestDistBox = g.NavMoveResultBestDistCenter = FLT_MAX; + g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + g.NavRefRectRel.Min, g.NavWindow->Pos + g.NavRefRectRel.Max) : ImRect(); + //g.OverlayDrawList.AddRect(g.NavRefRectScreen.Min, g.NavRefRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] + g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; +} + void ImGui::NewFrame() { ImGuiContext& g = *GImGui; @@ -2109,6 +2500,9 @@ void ImGui::NewFrame() for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f; + // Update directional navigation which may override MousePos if 'NavMovesMouse=true' + NavUpdate(); + // Update mouse input state if (g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0) g.IO.MousePos = ImVec2(-9999.0f, -9999.0f); @@ -2116,6 +2510,8 @@ void ImGui::NewFrame() g.IO.MouseDelta = ImVec2(0.0f, 0.0f); else g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; + if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f) + g.NavDisableMouseHover = false; g.IO.MousePosPrev = g.IO.MousePos; for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) { @@ -2143,6 +2539,8 @@ void ImGui::NewFrame() { g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i])); } + if (g.IO.MouseDown[i]) // Pressing any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation + g.NavDisableMouseHover = false; } // Calculate frame-rate for the user, as a purely luxurious feature @@ -2267,8 +2665,12 @@ void ImGui::NewFrame() // Pressing TAB activate widget focus //// NB: Don't discard FocusedWindow if it isn't active, so that a window that go on/off programatically won't lose its keyboard focus. // [2016/07/17] That comment was made invalid by 19d02becef94e8e0f1d432a8bd55cd783876583c - if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Active && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false)) - g.FocusedWindow->FocusIdxTabRequestNext = 0; + if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Active && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNav) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false)) + if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) + g.FocusedWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); + else + g.FocusedWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0; + g.NavIdTabCounter = INT_MAX; // Mark all windows as not visible for (int i = 0; i != g.Windows.Size; i++) @@ -2555,6 +2957,19 @@ static void AddWindowToRenderList(ImVector& out_render_list, ImGuiW } } +static void AddWindowToRenderListSelectLayer(ImGuiWindow* window) +{ + // FIXME: Generalize this with a proper layering system so e.g. user can draw in specific layers, below text, .. + ImGuiContext& g = *GImGui; + g.IO.MetricsActiveWindows++; + if (window->Flags & ImGuiWindowFlags_Popup) + AddWindowToRenderList(g.RenderDrawLists[1], window); + else if (window->Flags & ImGuiWindowFlags_Tooltip) + AddWindowToRenderList(g.RenderDrawLists[2], window); + else + AddWindowToRenderList(g.RenderDrawLists[0], window); +} + // When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result. void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect) { @@ -2606,11 +3021,16 @@ void ImGui::EndFrame() if (g.HoveredRootWindow != NULL) { FocusWindow(g.HoveredWindow); + if (g.NavWindow != g.HoveredWindow) + { + g.NavRefRectRel = ImRect(g.IO.MousePos - g.HoveredWindow->Pos, g.IO.MousePos - g.HoveredWindow->Pos); //ImRect(0,0,0,0); + g.NavDisableHighlight = true; + } if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove)) { g.MovedWindow = g.HoveredWindow; g.MovedWindowMoveId = g.HoveredRootWindow->MoveId; - SetActiveID(g.MovedWindowMoveId, g.HoveredRootWindow); + SetActiveIDNoNav(g.MovedWindowMoveId, g.HoveredRootWindow); } } else if (g.FocusedWindow != NULL && GetFrontMostModalRootWindow() == NULL) @@ -2632,6 +3052,7 @@ void ImGui::EndFrame() continue; AddWindowToSortedBuffer(g.WindowsSortBuffer, window); } + IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong g.Windows.swap(g.WindowsSortBuffer); @@ -2662,18 +3083,11 @@ void ImGui::Render() for (int i = 0; i != g.Windows.Size; i++) { ImGuiWindow* window = g.Windows[i]; - if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0) - { - // FIXME: Generalize this with a proper layering system so e.g. user can draw in specific layers, below text, .. - g.IO.MetricsActiveWindows++; - if (window->Flags & ImGuiWindowFlags_Popup) - AddWindowToRenderList(g.RenderDrawLists[1], window); - else if (window->Flags & ImGuiWindowFlags_Tooltip) - AddWindowToRenderList(g.RenderDrawLists[2], window); - else - AddWindowToRenderList(g.RenderDrawLists[0], window); - } + if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0 && window != g.NavWindowingTarget) + AddWindowToRenderListSelectLayer(window); } + if (g.NavWindowingTarget && g.NavWindowingTarget->Active && g.NavWindowingTarget->HiddenFrames <= 0) // NavWindowing target is always displayed front-most + AddWindowToRenderListSelectLayer(g.NavWindowingTarget); // Flatten layers int n = g.RenderDrawLists[0].Size; @@ -3262,6 +3676,12 @@ bool ImGui::IsItemActive() return false; } +bool ImGui::IsItemFocused() +{ + ImGuiContext& g = *GImGui; + return g.NavId && !g.NavDisableHighlight && g.NavId == g.CurrentWindow->DC.LastItemId; +} + bool ImGui::IsItemClicked(int mouse_button) { return IsMouseClicked(mouse_button) && IsItemHovered(); @@ -3277,6 +3697,11 @@ bool ImGui::IsAnyItemActive() return GImGui->ActiveId != 0; } +bool ImGui::IsAnyItemFocused() +{ + return GImGui->NavId != 0 && !GImGui->NavDisableHighlight; +} + bool ImGui::IsItemVisible() { ImGuiWindow* window = GetCurrentWindowRead(); @@ -3293,6 +3718,19 @@ void ImGui::SetItemAllowOverlap() g.ActiveIdAllowOverlap = true; } +void ImGui::SetItemDefaultFocus() +{ + ImGuiContext& g = *GImGui; + if (g.NavWindow == g.CurrentWindow && (g.NavInitDefaultRequest || g.NavInitDefaultResultId != 0)) + { + g.NavInitDefaultRequest = false; + g.NavInitDefaultResultExplicit = true; + g.NavInitDefaultResultId = g.CurrentWindow->DC.LastItemId; + if (!IsItemVisible()) + SetScrollHere(); + } +} + ImVec2 ImGui::GetItemRectMin() { ImGuiWindow* window = GetCurrentWindowRead(); @@ -3371,7 +3809,9 @@ void ImGui::OpenPopupEx(const char* str_id, bool reopen_existing) ImGuiWindow* window = g.CurrentWindow; ImGuiID id = window->GetID(str_id); int current_stack_size = g.CurrentPopupStack.Size; - ImGuiPopupRef popup_ref = ImGuiPopupRef(id, window, window->GetID("##menus"), g.IO.MousePos); // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here) + ImVec2 mouse_pos = g.IO.MousePos; + ImVec2 popup_pos = (g.ActiveIdSource == ImGuiInputSource_Mouse || g.ActiveId == 0) ? mouse_pos : window->DC.LastItemRect.GetCenter(); + ImGuiPopupRef popup_ref = ImGuiPopupRef(id, window, window->GetID("##menus"), popup_pos, mouse_pos); // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here) if (g.OpenPopupStack.Size < current_stack_size + 1) g.OpenPopupStack.push_back(popup_ref); else if (reopen_existing || g.OpenPopupStack[current_stack_size].PopupId != id) @@ -3600,6 +4040,14 @@ bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, if (!(parent_window->Flags & ImGuiWindowFlags_ShowBorders)) child_window->Flags &= ~ImGuiWindowFlags_ShowBorders; + // Process navigation-in immediately so NavInit can run on first frame + const ImGuiID id = parent_window->GetChildID(child_window); + if ((child_window->DC.NavHasItems || child_window->DC.NavHasScroll) && GImGui->NavActivateId == id) + { + FocusWindow(child_window); + NavInitWindow(child_window); + SetActiveIDNoNav(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item + } return ret; } @@ -3629,13 +4077,22 @@ void ImGui::EndChild() sz.x = ImMax(4.0f, sz.x); if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitY) sz.y = ImMax(4.0f, sz.y); - ImGui::End(); - window = GetCurrentWindow(); - ImRect bb(window->DC.CursorPos, window->DC.CursorPos + sz); + ImGuiWindow* parent_window = GetCurrentWindow(); + ImGuiID id = parent_window->GetChildID(window); + ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); ItemSize(sz); - ItemAdd(bb, NULL); + ItemAdd(bb, (window->DC.NavHasItems || window->DC.NavHasScroll) ? &id : NULL); + if (window->DC.NavHasItems || window->DC.NavHasScroll) + { + //if (!window->DC.NavHasItems && window->DC.NavHasScroll && g.NavWindow == window) // As a special case, we render nav highlight of child when inside when only scrolling is possible + //{ + // bb.Expand(-1.0f); + // id = g.NavId; + //} + RenderNavHighlight(id, bb); + } } } @@ -3862,6 +4319,8 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us } const bool window_appearing_after_being_hidden = (window->HiddenFrames == 1); + if (window_appearing_after_being_hidden) + window->NavLastId = 0; // Process SetNextWindow***() calls bool window_pos_set_by_api = false, window_size_set_by_api = false; @@ -3946,7 +4405,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us // Popup first latch mouse position, will position itself when it appears next frame window->AutoPosLastDirection = -1; if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api) - window->PosFloat = g.CurrentPopupStack.back().MousePosOnOpen; + window->PosFloat = g.CurrentPopupStack.back().PopupPosOnOpen; } // Collapse window by double-clicking on title bar @@ -4067,7 +4526,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us { IM_ASSERT(window_pos_set_by_api); ImRect rect_to_avoid; - if (parent_window->DC.MenuBarAppending) + if (parent_window && parent_window->DC.MenuBarAppending) rect_to_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight()); else rect_to_avoid = ImRect(parent_window->Pos.x + style.ItemSpacing.x, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - style.ItemSpacing.x - parent_window->ScrollbarSizes.x, FLT_MAX); // We want some overlap to convey the relative depth of each popup (here hard-coded to 4) @@ -4082,10 +4541,11 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us // Position tooltip (always follows mouse) if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api) { - ImRect rect_to_avoid(g.IO.MousePos.x - 16, g.IO.MousePos.y - 8, g.IO.MousePos.x + 24, g.IO.MousePos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead? - window->PosFloat = FindBestPopupWindowPos(g.IO.MousePos, window->Size, &window->AutoPosLastDirection, rect_to_avoid); + ImVec2 ref_pos = (g.ActiveId == 0 || g.ActiveIdSource == ImGuiInputSource_Mouse) ? g.IO.MousePos : window->DC.LastItemRect.GetCenter(); + ImRect rect_to_avoid(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24, ref_pos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead? + window->PosFloat = FindBestPopupWindowPos(ref_pos, window->Size, &window->AutoPosLastDirection, rect_to_avoid); if (window->AutoPosLastDirection == -1) - window->PosFloat = g.IO.MousePos + ImVec2(2,2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. + window->PosFloat = ref_pos + ImVec2(2,2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. } // Clamp position so it stays visible @@ -4133,6 +4593,16 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow()) window->DrawList->AddRectFilled(fullscreen_rect.Min, fullscreen_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); + // Navigation windowing (via ImGuiKey_NavWindowing key) shows whole window selected + // FIXME-NAVIGATION: Styling + if (g.NavWindowingTarget == window) + { + ImRect bb = window->Rect(); + bb.Expand(g.FontSize); + window->DrawList->AddRectFilled(bb.Min, bb.Max, IM_COL32(255,255,255,30)/*ImGui::GetColorU32(ImGuiCol_HeaderHovered, 0.15f)*/, g.Style.WindowRounding); + window->DrawList->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_HeaderHovered), g.Style.WindowRounding); + } + // Draw window + handle manual resize ImRect title_bar_rect = window->TitleBarRect(); const float window_rounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding; @@ -4152,28 +4622,47 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us const ImRect resize_rect(br - ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f), br); const ImGuiID resize_id = window->GetID("#RESIZE"); bool hovered, held; - ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds); - resize_col = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); - + ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds | ImGuiButtonFlags_NoNavOverride); if (hovered || held) g.MouseCursor = ImGuiMouseCursor_ResizeNWSE; + ImVec2 nav_resize_delta(0.0f, 0.0f); + if (g.NavWindowingTarget == window) + { + const float resize_speed = ImFloor(40 * g.FontSize * g.IO.DeltaTime); + if (IsKeyDownMap(ImGuiKey_NavLeft)) nav_resize_delta.x -= resize_speed; + if (IsKeyDownMap(ImGuiKey_NavRight)) nav_resize_delta.x += resize_speed; + if (IsKeyDownMap(ImGuiKey_NavUp)) nav_resize_delta.y -= resize_speed; + if (IsKeyDownMap(ImGuiKey_NavDown)) nav_resize_delta.y += resize_speed; + } + + ImVec2 size_target(FLT_MAX,FLT_MAX); if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0]) { // Manual auto-fit when double-clicking - ApplySizeFullWithConstraint(window, size_auto_fit); - if (!(flags & ImGuiWindowFlags_NoSavedSettings)) - MarkSettingsDirty(); + size_target = size_auto_fit; SetActiveID(0); } + else if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) + { + // FIXME-NAVIGATION: Should store and accumulate into a separate size buffer to handle sizing constraints properly + size_target = window->SizeFull + nav_resize_delta; + held = true; // For coloring + } else if (held) { // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - ApplySizeFullWithConstraint(window, (g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize()) - window->Pos); + size_target = (g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize()) - window->Pos; + } + + if (size_target.x != FLT_MAX && size_target.y != FLT_MAX) + { + ApplySizeFullWithConstraint(window, size_target); if (!(flags & ImGuiWindowFlags_NoSavedSettings)) MarkSettingsDirty(); } + resize_col = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); window->Size = window->SizeFull; title_bar_rect = window->TitleBarRect(); } @@ -4255,6 +4744,9 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->DC.CursorMaxPos = window->DC.CursorStartPos; window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f; window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; + window->DC.NavHasItems = window->DC.NavHasItemsNext; + window->DC.NavHasItemsNext = false; + window->DC.NavHasScroll = (GetScrollMaxY() > 0.0f); window->DC.MenuBarAppending = false; window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x); window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; @@ -4263,6 +4755,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled window->DC.AllowKeyboardFocus = true; + window->DC.AllowNavDefaultFocus = true; window->DC.ButtonRepeat = false; window->DC.ItemWidthStack.resize(0); window->DC.TextWrapPosStack.resize(0); @@ -4286,7 +4779,11 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us // New windows appears in front (we need to do that AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there) if (!window_was_active && !(flags & ImGuiWindowFlags_NoFocusOnAppearing)) if (!(flags & (ImGuiWindowFlags_ChildWindow|ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup)) + { FocusWindow(window); + IM_ASSERT(g.NavWindow == window); + NavInitWindow(window); + } // Title bar if (!(flags & ImGuiWindowFlags_NoTitleBar)) @@ -4329,16 +4826,23 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us */ } - // Inner clipping rectangle + // Inner rectangle and inner clipping rectangle // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior. const ImRect title_bar_rect = window->TitleBarRect(); const float border_size = window->BorderSize; - ImRect clip_rect; // Force round to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. - clip_rect.Min.x = ImFloor(0.5f + title_bar_rect.Min.x + ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f))); - clip_rect.Min.y = ImFloor(0.5f + title_bar_rect.Max.y + window->MenuBarHeight() + border_size); - clip_rect.Max.x = ImFloor(0.5f + window->Pos.x + window->Size.x - window->ScrollbarSizes.x - ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f))); - clip_rect.Max.y = ImFloor(0.5f + window->Pos.y + window->Size.y - window->ScrollbarSizes.y - border_size); + window->InnerRect.Min.x = title_bar_rect.Min.x; + window->InnerRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight(); + window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x; + window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y; + //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE); + + // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. + ImRect clip_rect; + clip_rect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f))); + clip_rect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + border_size); + clip_rect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f))); + clip_rect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - border_size); PushClipRect(clip_rect.Min, clip_rect.Max, true); // Clear 'accessed' flag last thing @@ -4437,7 +4941,7 @@ static void Scrollbar(ImGuiWindow* window, bool horizontal) bool held = false; bool hovered = false; const bool previously_held = (g.ActiveId == id); - ImGui::ButtonBehavior(bb, id, &hovered, &held); + ImGui::ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavOverride); float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v); float scroll_ratio = ImSaturate(scroll_v / scroll_max); @@ -4498,6 +5002,16 @@ void ImGui::FocusWindow(ImGuiWindow* window) { ImGuiContext& g = *GImGui; + if (g.FocusedWindow != window) + { + g.NavId = window ? window->NavLastId : 0; + g.NavIdIsAlive = false; + if (window && g.NavDisableMouseHover) + g.NavMousePosDirty = true; + g.NavRefRectRel.Min = g.NavRefRectRel.Max = window ? (window->DC.CursorStartPos - window->Pos) : ImVec2(0,0); + g.NavWindow = window; + } + // Passing NULL allow to disable keyboard focus g.FocusedWindow = window; if (!window) @@ -4524,6 +5038,24 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.Windows.push_back(window); } +// This needs to be called before we submit any widget (aka in or before Begin) +void ImGui::NavInitWindow(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastId == 0)) + { + g.NavId = window->NavLastId = 0; + g.NavInitDefaultRequest = true; + g.NavInitDefaultResultExplicit = false; + g.NavInitDefaultResultId = 0; + } + else + { + g.NavId = window->NavLastId; + } + g.NavWindow = window; +} + void ImGui::PushItemWidth(float item_width) { ImGuiWindow* window = GetCurrentWindow(); @@ -5497,9 +6029,13 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // PressedOnClick | | .. // PressedOnRelease | | .. (NOT on release) // PressedOnDoubleClick | | .. + // FIXME-NAVIGATION: We don't honor those different behaviors. if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0]) { - SetActiveID(id, window); // Hold on ID + if (flags & ImGuiButtonFlags_NoNavOverride) + SetActiveIDNoNav(id, window); + else + SetActiveID(id, window); // Hold on ID FocusWindow(window); g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; } @@ -5521,10 +6057,29 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true)) pressed = true; } + + if (pressed) + g.NavDisableHighlight = true; + } + + // Gamepad/Keyboard navigation + if (g.NavId == id && !g.NavDisableHighlight && (g.ActiveId == 0 || g.ActiveId == id)) + { + // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse + hovered = true; + if (IsKeyDownMap(ImGuiKey_NavActivate)) + { + // Set active id so it can be queried by user via IsItemActive(), etc. but don't react to it ourselves + g.NavActivateId = g.NavId; + SetActiveID(g.NavId, window); + g.ActiveIdAllowNavMove = true; + } + if (IsKeyPressedMap(ImGuiKey_NavActivate, (flags & ImGuiButtonFlags_Repeat) != 0)) + pressed = true; } bool held = false; - if (g.ActiveId == id) + if (g.ActiveId == id && g.ActiveIdSource == ImGuiInputSource_Mouse) { if (g.IO.MouseDown[0]) { @@ -5537,7 +6092,12 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool pressed = true; SetActiveID(0); } + if (!(flags & ImGuiButtonFlags_NoNavOverride)) + g.NavDisableHighlight = true; } + if (g.ActiveId == id && g.ActiveIdSource == ImGuiInputSource_Nav) + if (!IsKeyDownMap(ImGuiKey_NavActivate)) + SetActiveID(0); // 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 (hovered && (flags & ImGuiButtonFlags_AllowOverlapMode) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0)) @@ -5576,6 +6136,7 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags // Render const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + RenderNavHighlight(id, bb); RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, ImGuiAlign_Center | ImGuiAlign_VCenter); @@ -5629,6 +6190,12 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) ImGuiWindow* window = GetCurrentWindow(); const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius)); + const bool backup_allow_nav_default_focus = window->DC.AllowNavDefaultFocus; // We could exposethis as a flag to ItemAdd() but it is such a unique case for now + window->DC.AllowNavDefaultFocus = false; + const bool added = ItemAdd(bb, &id); // To allow navigation + window->DC.AllowNavDefaultFocus = backup_allow_nav_default_focus; + if (!added) + return false; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held); @@ -5703,6 +6270,7 @@ bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const I // Render const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + RenderNavHighlight(id, bb); RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding)); if (bg_col.w > 0.0f) window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col)); @@ -5916,9 +6484,9 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); if (pressed && !(flags & ImGuiTreeNodeFlags_Leaf)) { - bool toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)); + bool toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) || (g.NavActivateId == id); if (flags & ImGuiTreeNodeFlags_OpenOnArrow) - toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)); + toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)) && (!g.NavDisableMouseHover); if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) toggled |= g.IO.MouseDoubleClicked[0]; if (toggled) @@ -6326,7 +6894,7 @@ bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label ImGuiWindow* window = GetCurrentWindow(); // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen) - SetActiveID(g.ScalarAsInputTextId, window); + SetActiveIDNoNav(g.ScalarAsInputTextId, window); SetHoveredID(0); FocusableItemUnregister(window); @@ -6371,6 +6939,22 @@ int ImGui::ParseFormatPrecision(const char* fmt, int default_precision) return precision; } +// Adjustment delta for slider/drag/etc. +// FIXME-NAVIGATION: Accelerate over time? Expose more settings? Handle faster/slower modifiers here instead of widget level? +ImVec2 ImGui::NavGetTweakDelta() +{ + ImGuiContext& g = *GImGui; + float repeat_delay = g.IO.KeyRepeatDelay * 0.80f; + float repeat_rate = g.IO.KeyRepeatRate * 0.30f; + + ImVec2 delta(0.0f, 0.0f); + if (int count = GetKeyPressedAmount(g.IO.KeyMap[ImGuiKey_NavLeft], repeat_delay, repeat_rate)) delta.x = (float)-count; + if (int count = GetKeyPressedAmount(g.IO.KeyMap[ImGuiKey_NavRight], repeat_delay, repeat_rate)) delta.x = (float)+count; + if (int count = GetKeyPressedAmount(g.IO.KeyMap[ImGuiKey_NavUp], repeat_delay, repeat_rate)) delta.y = (float)-count; + if (int count = GetKeyPressedAmount(g.IO.KeyMap[ImGuiKey_NavDown], repeat_delay, repeat_rate)) delta.y = (float)+count; + return delta; +} + static float GetMinimumStepAtDecimalPrecision(int decimal_precision) { static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; @@ -6422,7 +7006,9 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v const ImGuiStyle& style = g.Style; // Draw frame - RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + const ImU32 frame_col = GetColorU32((g.ActiveId == id && g.ActiveIdSource == ImGuiInputSource_Nav) ? ImGuiCol_FrameBgActive : ImGuiCol_FrameBg); + RenderNavHighlight(id, frame_bb); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); const bool is_non_linear = (power < 1.0f-0.00001f) && (power > 1.0f-0.00001f); const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0; @@ -6457,13 +7043,48 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v bool value_changed = false; if (g.ActiveId == id) { - if (g.IO.MouseDown[0]) + bool set_new_value = false; + float normalized_pos = 0.0f; + if (g.ActiveIdSource == ImGuiInputSource_Mouse && g.IO.MouseDown[0]) { const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; - float normalized_pos = ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f); + normalized_pos = ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f); if (!is_horizontal) normalized_pos = 1.0f - normalized_pos; + set_new_value = true; + } + else if (g.ActiveIdSource == ImGuiInputSource_Nav && IsKeyDownMap(ImGuiKey_NavActivate)) + { + const ImVec2 delta2 = NavGetTweakDelta(); + if (float delta = is_horizontal ? delta2.x : -delta2.y) + { + normalized_pos = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos); + if (decimal_precision == 0 && !is_non_linear) + { + if (fabsf(v_max - v_min) <= 100.0f || IsKeyDownMap(ImGuiKey_NavTweakSlower)) + delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (v_max - v_min); // Gamepad/keyboard tweak speeds in integer steps + else + delta /= 100.0f; + } + else + { + delta /= 100.0f; // Gamepad/keyboard tweak speeds in % of slider bounds + if (IsKeyDownMap(ImGuiKey_NavTweakSlower)) + delta /= 10.0f; + } + if (IsKeyDownMap(ImGuiKey_NavTweakFaster)) + delta *= 10.0f; + normalized_pos = ImSaturate(normalized_pos + delta); // FIXME-NAVIGATION: todo: cancel adjustment if current value already past edge and we are moving in edge direction, to avoid clamping value to edge. + set_new_value = true; + } + } + else + { + SetActiveID(0); + } + if (set_new_value) + { float new_value; if (is_non_linear) { @@ -6501,10 +7122,6 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v value_changed = true; } } - else - { - SetActiveID(0); - } } // Draw @@ -6543,7 +7160,7 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); // NB- we don't call ItemSize() yet because we may turn into a text edit box below - if (!ItemAdd(total_bb, &id)) + if (!ItemAdd(total_bb, &id, &frame_bb)) { ItemSize(total_bb, style.FramePadding.y); return false; @@ -6559,13 +7176,12 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c // Tabbing or CTRL-clicking on Slider turns it into an input box bool start_text_input = false; - const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id); - if (tab_focus_requested || (hovered && g.IO.MouseClicked[0])) + const bool tab_focus_requested = FocusableItemRegister(window, id); + if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) { SetActiveID(id, window); FocusWindow(window); - - if (tab_focus_requested || g.IO.KeyCtrl) + if (tab_focus_requested || g.IO.KeyCtrl || g.NavInputId == id) { start_text_input = true; g.ScalarAsInputTextId = 0; @@ -6605,7 +7221,7 @@ bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); ItemSize(bb, style.FramePadding.y); - if (!ItemAdd(frame_bb, &id)) + if (!ItemAdd(frame_bb, &id, &frame_bb)) return false; const bool hovered = IsHovered(frame_bb, id); @@ -6616,7 +7232,7 @@ bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float display_format = "%.3f"; int decimal_precision = ParseFormatPrecision(display_format, 3); - if (hovered && g.IO.MouseClicked[0]) + if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) { SetActiveID(id, window); FocusWindow(window); @@ -6756,6 +7372,7 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s // Draw frame const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderNavHighlight(id, frame_bb); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); bool value_changed = false; @@ -6763,7 +7380,7 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s // Process clicking on the drag if (g.ActiveId == id) { - if (g.IO.MouseDown[0]) + if (g.IO.MouseDown[0] || IsKeyDownMap(ImGuiKey_NavActivate)) { if (g.ActiveIdIsJustActivated) { @@ -6777,12 +7394,25 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s float v_cur = g.DragCurrentValue; const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f); - float adjust_delta = mouse_drag_delta.x - g.DragLastMouseDelta.x; - if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f) - adjust_delta *= g.DragSpeedScaleFast; - if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f) - adjust_delta *= g.DragSpeedScaleSlow; + float adjust_delta = 0.0f; + if (g.ActiveIdSource == ImGuiInputSource_Mouse) + { + adjust_delta = mouse_drag_delta.x - g.DragLastMouseDelta.x; + if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f) + adjust_delta *= g.DragSpeedScaleFast; + if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f) + adjust_delta *= g.DragSpeedScaleSlow; + } + if (g.ActiveIdSource == ImGuiInputSource_Nav) + { + adjust_delta = NavGetTweakDelta().x; + if (IsKeyDownMap(ImGuiKey_NavTweakFaster) && g.DragSpeedScaleFast >= 0.0f) + adjust_delta *= 10.0f; + if (IsKeyDownMap(ImGuiKey_NavTweakSlower) && g.DragSpeedScaleSlow >= 0.0f) + adjust_delta /= 10.0f; + } adjust_delta *= v_speed; + g.DragLastMouseDelta.x = mouse_drag_delta.x; if (fabsf(adjust_delta) > 0.0f) { @@ -6800,7 +7430,6 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s { v_cur += adjust_delta; } - g.DragLastMouseDelta.x = mouse_drag_delta.x; // Clamp if (v_min < v_max) @@ -6842,7 +7471,7 @@ bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, f const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); // NB- we don't call ItemSize() yet because we may turn into a text edit box below - if (!ItemAdd(total_bb, &id)) + if (!ItemAdd(total_bb, &id, &frame_bb)) { ItemSize(total_bb, style.FramePadding.y); return false; @@ -6858,13 +7487,12 @@ bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, f // Tabbing or CTRL-clicking on Drag turns it into an input box bool start_text_input = false; - const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id); - if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] | g.IO.MouseDoubleClicked[0]))) + const bool tab_focus_requested = FocusableItemRegister(window, id); + if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || g.NavInputId == id) { SetActiveID(id, window); FocusWindow(window); - - if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0]) + if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id) { start_text_input = true; g.ScalarAsInputTextId = 0; @@ -7052,7 +7680,7 @@ void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_ge const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, NULL)) + if (!ItemAdd(total_bb, NULL, &frame_bb)) return; // Determine scale from values if not specified @@ -7242,6 +7870,7 @@ bool ImGui::Checkbox(const char* label, bool* v) if (pressed) *v = !(*v); + RenderNavHighlight(id, total_bb); RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); if (*v) { @@ -7308,6 +7937,7 @@ bool ImGui::RadioButton(const char* label, bool active) bool hovered, held; bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); + RenderNavHighlight(id, total_bb); window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16); if (active) { @@ -7634,7 +8264,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 else { ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, &id)) + if (!ItemAdd(total_bb, &id, &frame_bb)) return false; } @@ -7662,7 +8292,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 const bool is_shift_down = io.KeyShift; const bool is_alt_down = io.KeyAlt; const bool is_super_down = io.KeySuper; - const bool focus_requested = FocusableItemRegister(window, g.ActiveId == id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing + const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent); const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; @@ -7676,8 +8306,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY"); bool clear_active_id = false; - bool select_all = (g.ActiveId != id) && (flags & ImGuiInputTextFlags_AutoSelectAll) != 0; - if (focus_requested || user_clicked || user_scrolled) + + bool select_all = (g.ActiveId != id) && (((flags & ImGuiInputTextFlags_AutoSelectAll) != 0) || (g.NavInputId == id)); + if (focus_requested || user_clicked || user_scrolled || g.NavInputId == id) { if (g.ActiveId != id) { @@ -7815,7 +8446,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 { bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; if (!is_multiline || (ctrl_enter_for_new_line && !is_ctrl_down) || (!ctrl_enter_for_new_line && is_ctrl_down)) + { enter_pressed = clear_active_id = true; + } else if (is_editable) { unsigned int c = '\n'; // Insert new line @@ -7995,6 +8628,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on. const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL; + RenderNavHighlight(id, frame_bb); if (!is_multiline) RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); @@ -8408,17 +9042,18 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, &id)) + if (!ItemAdd(total_bb, &id, &frame_bb)) return false; const float arrow_size = (g.FontSize + style.FramePadding.x * 2.0f); const bool hovered = IsHovered(frame_bb, id); + const bool navigated = g.NavId == id; bool popup_open = IsPopupOpen(id); - bool popup_opened_now = false; const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f)); + RenderNavHighlight(id, frame_bb); RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_open || hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING + RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_open || hovered || navigated ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING RenderCollapseTriangle(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y) + style.FramePadding, true); if (*current_item >= 0 && *current_item < items_count) @@ -8431,22 +9066,30 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi if (label_size.x > 0) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + bool popup_toggled = false; if (hovered) { SetHoveredID(id); if (g.IO.MouseClicked[0]) { SetActiveID(0); - if (IsPopupOpen(id)) - { - ClosePopup(id); - } - else - { - FocusWindow(window); - OpenPopup(label); - popup_open = popup_opened_now = true; - } + popup_toggled = true; + } + } + if (g.NavActivateId == id) + popup_toggled = true; + if (popup_toggled) + { + if (IsPopupOpen(id)) + { + ClosePopup(id); + } + else + { + window->NavLastId = id; + FocusWindow(window); + OpenPopup(label); + popup_open = true; } } @@ -8475,6 +9118,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi if (BeginPopupEx(label, flags)) { // Display items + // FIXME-OPT: Use clipper Spacing(); for (int i = 0; i < items_count; i++) { @@ -8489,8 +9133,8 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi value_changed = true; *current_item = i; } - if (item_selected && popup_opened_now) - SetScrollHere(); + if (item_selected && popup_toggled) + SetItemDefaultFocus(); //SetScrollHere(); PopID(); } EndPopup(); @@ -8540,7 +9184,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl bb_with_spacing.Min.y -= spacing_U; bb_with_spacing.Max.x += spacing_R; bb_with_spacing.Max.y += spacing_D; - if (!ItemAdd(bb_with_spacing, &id)) + if (!ItemAdd(bb_with_spacing, (flags & ImGuiSelectableFlags_Disabled) ? NULL : &id)) { if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1) PushColumnClipRect(); @@ -8662,7 +9306,7 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper. bool value_changed = false; - ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); + ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to. while (clipper.Step()) for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { @@ -8677,6 +9321,8 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v *current_item = i; value_changed = true; } + if (item_selected) + SetItemDefaultFocus(); PopID(); } ListBoxFooter(); @@ -8834,7 +9480,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) { // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. bool moving_within_opened_triangle = false; - if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window) + if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar)) { if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window) { @@ -8853,6 +9499,12 @@ bool ImGui::BeginMenu(const char* label, bool enabled) want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle); want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed); + + if (g.NavActivateId == id) + { + want_close = menu_is_open; + want_open = !menu_is_open; + } } else if (menu_is_open && pressed && menuset_is_open) // menu-bar: click open menu to close { @@ -8911,9 +9563,10 @@ bool ImGui::ColorButton(const ImVec4& col, bool small_height, bool outline_borde bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held); + RenderNavHighlight(id, bb); RenderFrame(bb.Min, bb.Max, GetColorU32(col), outline_border, style.FrameRounding); - if (hovered) + if (g.HoveredId == id) SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col.x, col.y, col.z, col.w, IM_F32_TO_INT8_SAT(col.x), IM_F32_TO_INT8_SAT(col.y), IM_F32_TO_INT8_SAT(col.z), IM_F32_TO_INT8_SAT(col.z)); return pressed; @@ -9733,6 +10386,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) NodeDrawList(window->DrawList, "DrawList"); ImGui::BulletText("Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y); + ImGui::BulletText("NavLastId: 0x%08x, NavHasItems: %d", window->NavLastId, window->DC.NavHasItems); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); @@ -9763,7 +10417,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); ImGui::Text("HoveredId: 0x%08X/0x%08X", g.HoveredId, g.HoveredIdPreviousFrame); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not - ImGui::Text("ActiveId: 0x%08X/0x%08X", g.ActiveId, g.ActiveIdPreviousFrame); + ImGui::Text("ActiveId: 0x%08X/0x%08X, ActiveIdWindow: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); + ImGui::Text("NavId: 0x%08X, NavWindow: '%s'", g.NavId, g.NavWindow ? g.NavWindow->Name : "NULL"); + ImGui::Text("NavRefRectRel: (%.1f,%.1f)(%.1f,%.1f)", g.NavRefRectRel.Min.x, g.NavRefRectRel.Min.y, g.NavRefRectRel.Max.x, g.NavRefRectRel.Max.y); + ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); ImGui::TreePop(); } } diff --git a/imgui.h b/imgui.h index 994b663c..3a9b1e69 100644 --- a/imgui.h +++ b/imgui.h @@ -167,7 +167,7 @@ namespace ImGui IMGUI_API void SetScrollY(float scroll_y); // set scrolling amount [0..GetScrollMaxY()] IMGUI_API void SetScrollHere(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. IMGUI_API void SetScrollFromPosY(float pos_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position valid. use GetCursorPos() or GetCursorStartPos()+offset to get valid positions. - IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use negative 'offset' to access previous widgets. + IMGUI_API void SetKeyboardFocusHere(int offset = 0); // FIXME-NAVIGATION // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use negative 'offset' to access previous widgets. IMGUI_API void SetStateStorage(ImGuiStorage* tree); // replace tree state storage with our own (if you want to manipulate it yourself, typically clear subsection of it) IMGUI_API ImGuiStorage* GetStateStorage(); @@ -396,14 +396,17 @@ namespace ImGui IMGUI_API bool IsItemHovered(); // is the last item hovered by mouse, and usable? IMGUI_API bool IsItemHoveredRect(); // is the last item hovered by mouse? even if another item is active or window is blocked by popup while we are hovering this IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited- items that don't interact will always return false) + 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) IMGUI_API bool IsItemVisible(); // is the last item visible? (aka not out of sight due to clipping/scrolling.) IMGUI_API bool IsAnyItemHovered(); IMGUI_API bool IsAnyItemActive(); + IMGUI_API bool IsAnyItemFocused(); IMGUI_API ImVec2 GetItemRectMin(); // get bounding rect of last item in screen space IMGUI_API ImVec2 GetItemRectMax(); // " IMGUI_API ImVec2 GetItemRectSize(); // " IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. + IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window IMGUI_API bool IsWindowFocused(); // is current window focused IMGUI_API bool IsWindowHovered(); // is current window hovered and hoverable (not blocked by a popup) (differentiate child windows from each others) IMGUI_API bool IsWindowHoveredRect(); // is current window hovered, disregarding of any consideration of being blocked by a popup. (unlike IsWindowHovered() this will return true even if the window is blocked because of a popup) @@ -506,6 +509,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14, // Always show vertical scrollbar (even if ContentSize.y < Size.y) ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, // Always show horizontal scrollbar (even if ContentSize.x < Size.x) ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) + ImGuiWindowFlags_NoNav = 1 << 17, // No directional gamepad/keyboard navigation // [Internal] ImGuiWindowFlags_ChildWindow = 1 << 20, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_ChildWindowAutoFitX = 1 << 21, // Don't use! For internal use by BeginChild() @@ -590,6 +594,19 @@ enum ImGuiKey_ ImGuiKey_X, // for text edit CTRL+X: cut ImGuiKey_Y, // for text edit CTRL+Y: redo ImGuiKey_Z, // for text edit CTRL+Z: undo + + // Inputs for Gamepad/Keyboard navigation. Feed those buttons with the input of either or both peripherals involved. + ImGuiKey_NavActivate, // press button, tweak value // e.g. Space key, Circle button + ImGuiKey_NavCancel, // close menu/popup/child, unselect // e.g. Escape key, Cross button + ImGuiKey_NavInput, // text input // e.g. Enter key, Triangle button + ImGuiKey_NavWindowing, // change focus, move, resize // e.g. Square button + ImGuiKey_NavLeft, // e.g. Left arrow, D-Pad left + 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_NavTweakFaster,// e.g. Shift key, R-trigger + ImGuiKey_NavTweakSlower,// e.g. Alt key, L-trigger + ImGuiKey_COUNT }; @@ -751,6 +768,7 @@ struct ImGuiIO int KeyMap[ImGuiKey_COUNT]; // // Map of indices into the KeysDown[512] entries array float KeyRepeatDelay; // = 0.250f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). float KeyRepeatRate; // = 0.020f // When holding a key/button, rate at which it repeats, in seconds. + bool NavMovesMouse; // = false // Directional navigation move the mouse cursor (update MousePos and set void* UserData; // = NULL // Store your own data for retrieval by callbacks. ImFontAtlas* Fonts; // // Load and assemble one or more fonts into a single tightly packed texture. Output to Fonts array. @@ -817,6 +835,9 @@ struct ImGuiIO bool WantCaptureMouse; // Mouse is hovering a window or widget is active (= ImGui will use your mouse input) bool WantCaptureKeyboard; // Widget is active (= ImGui will use your keyboard input) bool WantTextInput; // Text input widget is active, which will read input characters from the InputCharacters array. + bool WantMoveMouse; // MousePos has been altered, used only if 'NavMovesMouse=true', back-end can reposition mouse on next frame. + bool NavUsable; // Directional navigation is currently allowed (ImGuiKey_NavXXX events). + bool NavActive; // Directional navigation is active/visible and currently allowed (ImGuiKey_NavXXX events). float Framerate; // Framerate estimation, in frame per second. Rolling average estimation based on IO.DeltaTime over 120 frames int MetricsAllocs; // Number of active memory allocations int MetricsRenderVertices; // Vertices output during last call to Render() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3862e628..c17537a0 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -153,6 +153,7 @@ void ImGui::ShowTestWindow(bool* p_open) static bool no_scrollbar = false; static bool no_collapse = false; static bool no_menu = false; + static bool no_nav = false; // Demonstrate the various window flags. Typically you would just use the default. ImGuiWindowFlags window_flags = 0; @@ -163,6 +164,7 @@ void ImGui::ShowTestWindow(bool* p_open) if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar; if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar; + if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; ImGui::SetNextWindowSize(ImVec2(550,680), ImGuiSetCond_FirstUseEver); if (!ImGui::Begin("ImGui Demo", p_open, window_flags)) { @@ -224,7 +226,8 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::Checkbox("No move", &no_move); ImGui::SameLine(150); ImGui::Checkbox("No scrollbar", &no_scrollbar); ImGui::SameLine(300); ImGui::Checkbox("No collapse", &no_collapse); - ImGui::Checkbox("No menu", &no_menu); + ImGui::Checkbox("No menu", &no_menu); ImGui::SameLine(150); + ImGui::Checkbox("No nav", &no_nav); if (ImGui::TreeNode("Style")) { @@ -1297,6 +1300,7 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::PopStyleVar(); if (ImGui::Button("OK", ImVec2(120,0))) { ImGui::CloseCurrentPopup(); } + ImGui::SetItemDefaultFocus(); ImGui::SameLine(); if (ImGui::Button("Cancel", ImVec2(120,0))) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); @@ -1498,6 +1502,9 @@ void ImGui::ShowTestWindow(bool* p_open) if (ImGui::CollapsingHeader("Keyboard, Mouse & Focus")) { ImGuiIO& io = ImGui::GetIO(); + ImGui::Checkbox("io.NavMovesMouse", &io.NavMovesMouse); + ImGui::SameLine(); ShowHelpMarker("Request ImGui to move your move cursor when using gamepad/keyboard navigation. NewFrame() will change io.MousePos and set the io.WantMoveMouse flag, your backend will need to apply the new mouse position."); + ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); ImGui::SameLine(); ShowHelpMarker("Request ImGui to render a mouse cursor for you in software. Note that a mouse cursor rendered via regular GPU rendering will feel more laggy than hardware cursor, but will be more in sync with your other visuals."); @@ -1581,6 +1588,8 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::Text("WantCaptureMouse: %d", io.WantCaptureMouse); ImGui::Text("WantCaptureKeyboard: %d", io.WantCaptureKeyboard); ImGui::Text("WantTextInput: %d", io.WantTextInput); + ImGui::Text("WantMoveMouse: %d", io.WantMoveMouse); + ImGui::Text("NavUsable: %d, NavActive: %d", io.NavUsable, io.NavActive); ImGui::Button("Hovering me sets the\nkeyboard capture flag"); if (ImGui::IsItemHovered()) @@ -1602,7 +1611,7 @@ void ImGui::ShowTestWindow(bool* p_open) char label[32]; sprintf(label, "Mouse cursor %d", i); ImGui::Bullet(); ImGui::Selectable(label, false); - if (ImGui::IsItemHovered()) + if (ImGui::IsItemHovered() || ImGui::IsItemFocused()) ImGui::SetMouseCursor(i); } ImGui::TreePop(); diff --git a/imgui_internal.h b/imgui_internal.h index 577993dc..6180891d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -13,6 +13,7 @@ #include // FILE* #include // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf +#include // INT_MIN, INT_MAX #ifdef _MSC_VER #pragma warning (push) @@ -159,7 +160,8 @@ enum ImGuiButtonFlags_ ImGuiButtonFlags_Disabled = 1 << 7, // disable interaction ImGuiButtonFlags_AlignTextBaseLine = 1 << 8, // vertically align button to match text baseline - ButtonEx() only ImGuiButtonFlags_NoKeyModifiers = 1 << 9, // disable interaction if a key modifier is held - ImGuiButtonFlags_AllowOverlapMode = 1 << 10 // require previous frame HoveredId to either match id or be null before being usable + ImGuiButtonFlags_AllowOverlapMode = 1 << 10, // require previous frame HoveredId to either match id or be null before being usable + ImGuiButtonFlags_NoNavOverride = 1 << 11 // don't override navigation id when activated }; enum ImGuiSliderFlags_ @@ -195,6 +197,22 @@ enum ImGuiDataType ImGuiDataType_Float }; +enum ImGuiInputSource +{ + ImGuiInputSource_None = 0, + ImGuiInputSource_Mouse, + ImGuiInputSource_Nav, +}; + +enum ImGuiNavDir +{ + ImGuiNavDir_None = -1, + ImGuiNavDir_W, // Needs to be 0..3 (using arithmetic op in NavScoreItem()) + ImGuiNavDir_E, + ImGuiNavDir_N, + ImGuiNavDir_S +}; + // 2D axis aligned bounding-box // NB: we can't rely on ImVec2 math operators being available here struct IMGUI_API ImRect @@ -335,9 +353,10 @@ struct ImGuiPopupRef ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() ImGuiWindow* ParentWindow; // Set on OpenPopup() ImGuiID ParentMenuSet; // Set on OpenPopup() + ImVec2 PopupPosOnOpen; // Preferred popup position (typically == MousePosOnOpen when using mouse) ImVec2 MousePosOnOpen; // Copy of mouse position at the time of opening popup - ImGuiPopupRef(ImGuiID id, ImGuiWindow* parent_window, ImGuiID parent_menu_set, const ImVec2& mouse_pos) { PopupId = id; Window = NULL; ParentWindow = parent_window; ParentMenuSet = parent_menu_set; MousePosOnOpen = mouse_pos; } + ImGuiPopupRef(ImGuiID id, ImGuiWindow* parent_window, ImGuiID parent_menu_set, const ImVec2& popup_pos, const ImVec2& mouse_pos) { PopupId = id; Window = NULL; ParentWindow = parent_window; ParentMenuSet = parent_menu_set; PopupPosOnOpen = popup_pos; MousePosOnOpen = mouse_pos; } }; // Main state for ImGui @@ -369,9 +388,11 @@ struct ImGuiContext ImGuiID ActiveIdPreviousFrame; bool ActiveIdIsAlive; // Active widget has been seen this frame bool ActiveIdIsJustActivated; // Set at the time of activation for one frame + bool ActiveIdAllowNavMove; // Active widget allows using directional navigation (e.g. can activate a button and move away from it) bool ActiveIdAllowOverlap; // Active widget allows another widget to steal active id (generally for overlapping widgets, but not always) ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) ImGuiWindow* ActiveIdWindow; + ImGuiInputSource ActiveIdSource; // Activating with mouse or nav (gamepad/keyboard) ImGuiWindow* MovedWindow; // Track the child window we clicked on to move a window. ImGuiID MovedWindowMoveId; // == MovedWindow->RootWindow->MoveId ImVector Settings; // .ini Settings @@ -382,6 +403,29 @@ struct ImGuiContext ImVector OpenPopupStack; // Which popups are open (persistent) ImVector CurrentPopupStack; // Which level of BeginPopup() we are in (reset every frame) + // Navigation data (for gamepad/keyboard) + ImGuiID NavId; // Nav/focused widget for navigation + ImGuiID NavActivateId, NavInputId; // ~~ IsKeyPressedMap(ImGuiKey_NavActive) ? NavId : 0, etc. (to make widget code terser) + ImGuiID NavTabbedId; // + ImRect NavRefRectRel, NavScoringRectScreen;// Reference rectangle, in window space. Modified rectangle for directional navigation scoring, in screen space. + ImGuiWindow* NavWindow; // + ImGuiWindow* NavWindowingTarget; + int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing + bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid + bool NavMousePosDirty; + bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard functionalities + bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we disable mouse hovering until mouse is touched again + bool NavInitDefaultRequest; // Init request for appearing window to select first item + ImGuiID NavInitDefaultResultId; + bool NavInitDefaultResultExplicit; // Whether the result was explicitly requested with SetItemDefaultFocus() + bool NavMoveRequest; // Move request for this frame + ImGuiNavDir NavMoveDir; // West/East/North/South + ImGuiID NavMoveResultBestId; // Best move request candidate + float NavMoveResultBestDistBox; // Best move request candidate box distance to current NavId + float NavMoveResultBestDistCenter; // Best move request candidate center distance to current NavId + float NavMoveResultBestDistAxial; + ImRect NavMoveResultBestRefRectRel; // Best move request candidate bounding box in window space + // Storage for SetNexWindow** and SetNextTreeNode*** functions ImVec2 SetNextWindowPosVal; ImVec2 SetNextWindowSizeVal; @@ -461,10 +505,28 @@ struct ImGuiContext ActiveIdAllowOverlap = false; ActiveIdClickOffset = ImVec2(-1,-1); ActiveIdWindow = NULL; + ActiveIdSource = ImGuiInputSource_None; MovedWindow = NULL; MovedWindowMoveId = 0; SettingsDirtyTimer = 0.0f; + NavId = NavActivateId = NavInputId = NavTabbedId = 0; + NavRefRectRel = NavScoringRectScreen = ImRect(); + NavWindow = NULL; + NavWindowingTarget = NULL; + NavIdTabCounter = INT_MAX; + NavIdIsAlive = false; + NavMousePosDirty = false; + NavDisableHighlight = true; + NavDisableMouseHover = false; + NavInitDefaultRequest = false; + NavInitDefaultResultId = 0; + NavInitDefaultResultExplicit = false; + NavMoveRequest = false; + NavMoveDir = ImGuiNavDir_None; + NavMoveResultBestId = 0; + NavMoveResultBestDistBox = NavMoveResultBestDistCenter = NavMoveResultBestDistAxial = 0.0f; + SetNextWindowPosVal = ImVec2(0.0f, 0.0f); SetNextWindowSizeVal = ImVec2(0.0f, 0.0f); SetNextWindowCollapsedVal = false; @@ -526,6 +588,8 @@ struct IMGUI_API ImGuiDrawContext ImRect LastItemRect; bool LastItemHoveredAndUsable; // Item rectangle is hovered, and its window is currently interactable with (not blocked by a popup preventing access to the window) bool LastItemHoveredRect; // Item rectangle is hovered, but its window may or not be currently interactable with (might be blocked by a popup preventing access to the window) + bool NavHasItems, NavHasItemsNext; // Set when has any navigatable item + bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) bool MenuBarAppending; float MenuBarOffsetX; ImVector ChildWindows; @@ -536,6 +600,7 @@ struct IMGUI_API ImGuiDrawContext float ItemWidth; // == ItemWidthStack.back(). 0.0: default, >0.0: width in pixels, <0.0: align xx pixels to the right of window float TextWrapPos; // == TextWrapPosStack.back() [empty == -1.0f] bool AllowKeyboardFocus; // == AllowKeyboardFocusStack.back() [empty == true] + bool AllowNavDefaultFocus; // (not exposed via stack) bool ButtonRepeat; // == ButtonRepeatStack.back() [empty == false] ImVector ItemWidthStack; ImVector TextWrapPosStack; @@ -568,6 +633,7 @@ struct IMGUI_API ImGuiDrawContext LastItemId = 0; LastItemRect = ImRect(0.0f,0.0f,0.0f,0.0f); LastItemHoveredAndUsable = LastItemHoveredRect = false; + NavHasItems = NavHasItemsNext = NavHasScroll = false; MenuBarAppending = false; MenuBarOffsetX = 0.0f; StateStorage = NULL; @@ -620,6 +686,7 @@ struct IMGUI_API ImGuiWindow bool SkipItems; // == Visible && !Collapsed int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) + ImGuiID NavLastId; // Last known NavId for this window int AutoFitFramesX, AutoFitFramesY; bool AutoFitOnlyGrows; int AutoPosLastDirection; @@ -633,6 +700,7 @@ struct IMGUI_API ImGuiWindow ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack ImRect ClipRect; // = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2. ImRect WindowRectClipped; // = WindowRect just after setup in Begin(). == window->Rect() for root window. + ImRect InnerRect; int LastFrameActive; float ItemWidthDefault; ImGuiSimpleColumns MenuColumns; // Simplified columns storage for menu items @@ -644,6 +712,7 @@ struct IMGUI_API ImGuiWindow ImGuiWindow* ParentWindow; // Immediate parent in the window stack *regardless* of whether this window is a child window or not) // Navigation / Focus + // FIXME-NAVIGATION: Merge all this with the new Nav system, at least the request variables should be moved to ImGuiContext int FocusIdxAllCounter; // Start at -1 and increase as assigned via FocusItemRegister() int FocusIdxTabCounter; // (same, but only count widgets which you can Tab through) int FocusIdxAllRequestCurrent; // Item being requested for focus @@ -658,6 +727,7 @@ public: ImGuiID GetID(const char* str, const char* str_end = NULL); ImGuiID GetID(const void* ptr); ImGuiID GetIDNoKeepAlive(const char* str, const char* str_end = NULL); + ImGuiID GetChildID(ImGuiWindow* window); ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x+Size.x, Pos.y+Size.y); } float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale; } @@ -687,21 +757,25 @@ namespace ImGui IMGUI_API void EndFrame(); // Ends the ImGui frame. Automatically called by Render()! you most likely don't need to ever call that yourself directly. If you don't need to render you can call EndFrame() but you'll have wasted CPU already. If you don't need to render, don't create any windows instead! IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); + IMGUI_API void SetActiveIDNoNav(ImGuiID id, ImGuiWindow* window); IMGUI_API void SetHoveredID(ImGuiID id); IMGUI_API void KeepAliveID(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); - IMGUI_API bool ItemAdd(const ImRect& bb, const ImGuiID* id); + IMGUI_API bool ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb = NULL); IMGUI_API bool IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged); IMGUI_API bool IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs = false); - IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop = true); // Return true if focus is requested + IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop = true); // Return true if focus is requested IMGUI_API void FocusableItemUnregister(ImGuiWindow* window); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API void OpenPopupEx(const char* str_id, bool reopen_existing); + IMGUI_API void NavInitWindow(ImGuiWindow* window); + IMGUI_API ImVec2 NavGetTweakDelta(); + 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); } From 2cab404a81e022941376450131e78e014eb28e42 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 23 Jul 2016 15:44:56 +0200 Subject: [PATCH 021/319] Fixed tooltip positioning issues when using navigation (#323) --- imgui.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ad2007bd..cb10ab84 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2264,6 +2264,14 @@ int ImGui::GetFrameCount() return GImGui->FrameCount; } +static ImVec2 NavCalcPreferredMousePos() +{ + ImGuiContext& g = *GImGui; + if (g.NavWindow) + return g.NavWindow->Pos + ImVec2(g.NavRefRectRel.Min.x + ImMin(g.Style.FramePadding.x*4, g.NavRefRectRel.GetWidth()), g.NavRefRectRel.Max.y - ImMin(g.Style.FramePadding.y, g.NavRefRectRel.GetHeight())); + return g.IO.MousePos; +} + static void NavUpdate() { ImGuiContext& g = *GImGui; @@ -2274,7 +2282,7 @@ static void NavUpdate() // Set mouse position given our knowledge of the nav widget position from last frame if (g.IO.NavMovesMouse) { - g.IO.MousePos = g.IO.MousePosPrev = g.NavWindow->Pos + ImVec2(g.NavRefRectRel.Min.x + ImMin(g.Style.FramePadding.x*4, g.NavRefRectRel.GetWidth()), g.NavRefRectRel.Max.y - ImMin(g.Style.FramePadding.y, g.NavRefRectRel.GetHeight())); + g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredMousePos(); g.IO.WantMoveMouse = true; } g.NavMousePosDirty = false; @@ -3810,7 +3818,7 @@ void ImGui::OpenPopupEx(const char* str_id, bool reopen_existing) ImGuiID id = window->GetID(str_id); int current_stack_size = g.CurrentPopupStack.Size; ImVec2 mouse_pos = g.IO.MousePos; - ImVec2 popup_pos = (g.ActiveIdSource == ImGuiInputSource_Mouse || g.ActiveId == 0) ? mouse_pos : window->DC.LastItemRect.GetCenter(); + ImVec2 popup_pos = (g.ActiveIdSource == ImGuiInputSource_Nav) ? NavCalcPreferredMousePos() : mouse_pos; ImGuiPopupRef popup_ref = ImGuiPopupRef(id, window, window->GetID("##menus"), popup_pos, mouse_pos); // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here) if (g.OpenPopupStack.Size < current_stack_size + 1) g.OpenPopupStack.push_back(popup_ref); @@ -4541,7 +4549,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us // Position tooltip (always follows mouse) if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api) { - ImVec2 ref_pos = (g.ActiveId == 0 || g.ActiveIdSource == ImGuiInputSource_Mouse) ? g.IO.MousePos : window->DC.LastItemRect.GetCenter(); + ImVec2 ref_pos = (g.ActiveIdSource == ImGuiInputSource_Nav) ? NavCalcPreferredMousePos() : g.IO.MousePos; ImRect rect_to_avoid(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24, ref_pos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead? window->PosFloat = FindBestPopupWindowPos(ref_pos, window->Size, &window->AutoPosLastDirection, rect_to_avoid); if (window->AutoPosLastDirection == -1) From ad483090474b5314b1e2d437b1722b8f9a7ed6c8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 23 Jul 2016 16:01:27 +0200 Subject: [PATCH 022/319] Navigation: Fixed using NavWindowed when a menu is open. (#323) --- imgui.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cb10ab84..37f7461d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2343,7 +2343,7 @@ static void NavUpdate() // Navigation windowing mode (change focus, move/resize window) if (!g.NavWindowingTarget && g.NavWindow && IsKeyPressedMap(ImGuiKey_NavWindowing, false)) - g.NavWindowingTarget = g.NavWindow->RootWindow; + g.NavWindowingTarget = g.NavWindow->RootNonPopupWindow; if (g.NavWindowingTarget) { // FIXME-NAVIGATION: Need to clarify input semantic, naming is misleading/incorrect here. @@ -2357,10 +2357,10 @@ static void NavUpdate() i_current = i; int i_target = -1; for (int i = i_current+focus_change_dir; i >= 0 && i < g.Windows.Size && i_target == -1; i += focus_change_dir) - if (g.Windows[i]->Active && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow)) + if (g.Windows[i]->Active && g.Windows[i] == g.Windows[i]->RootNonPopupWindow) i_target = i; for (int i = (focus_change_dir < 0) ? (g.Windows.Size-1) : 0; i >= 0 && i < g.Windows.Size && i_target == -1 && i_target != i_current; i += focus_change_dir) - if (g.Windows[i]->Active && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow)) + if (g.Windows[i]->Active && g.Windows[i] == g.Windows[i]->RootNonPopupWindow) i_target = i; if (i_target != -1) { @@ -2369,12 +2369,12 @@ static void NavUpdate() } } - // End window select/focus mode and apply final focus + // Apply actual focus only when leaving NavWindowing mode (until then the window was merely rendered front-most) if (!IsKeyDownMap(ImGuiKey_NavWindowing)) { if (g.NavWindowingTarget) - if (!g.FocusedWindow || (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow != g.FocusedWindow->RootWindow)) - ImGui::FocusWindow(g.NavWindowingTarget->RootWindow); + if (!g.FocusedWindow || (g.NavWindowingTarget->RootNonPopupWindow != g.FocusedWindow->RootNonPopupWindow)) + ImGui::FocusWindow(g.NavWindowingTarget->RootNonPopupWindow); g.NavWindowingTarget = NULL; } } @@ -4387,7 +4387,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us break; window->ParentWindow = parent_window; window->RootWindow = g.CurrentWindowStack[root_idx]; - window->RootNonPopupWindow = g.CurrentWindowStack[root_non_popup_idx]; // This is merely for displaying the TitleBgActive color. + window->RootNonPopupWindow = g.CurrentWindowStack[root_non_popup_idx]; // Used e.g. to keep displaying TitleBgActive color when menu is active, and for NavWindowing // When reusing window again multiple times a frame, just append content (don't need to setup again) if (first_begin_of_the_frame) From 2f1fb415796fa8cc0b7a8fe683e583199b89d2f9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 23 Jul 2016 16:31:23 +0200 Subject: [PATCH 023/319] Navigation: Fixed automatic fallback scrolling from glitching near scroll limits because of miscalculation of SizeContents (#323) --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 37f7461d..7e3412f6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2446,7 +2446,7 @@ static void NavUpdate() if (g.FocusedWindow && !g.FocusedWindow->DC.NavHasItems && g.FocusedWindow->DC.NavHasScroll && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNav) && g.NavMoveRequest && (g.NavMoveDir == ImGuiNavDir_N || g.NavMoveDir == ImGuiNavDir_S)) { 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. - g.FocusedWindow->Scroll.y = ImFloor(g.FocusedWindow->Scroll.y + ((g.NavMoveDir == ImGuiNavDir_N) ? -1.0f : +1.0f) * scroll_speed); + SetWindowScrollY(g.FocusedWindow, ImFloor(g.FocusedWindow->Scroll.y + ((g.NavMoveDir == ImGuiNavDir_N) ? -1.0f : +1.0f) * scroll_speed)); } // Reset search @@ -5372,7 +5372,7 @@ ImVec2 ImGui::GetWindowPos() static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y) { - window->DC.CursorMaxPos.y += window->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. window->Scroll.y = new_scroll_y; window->DC.CursorMaxPos.y -= window->Scroll.y; } From 0cc20fca831e60b4898cf66e3ac595d8bdebb830 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 23 Jul 2016 19:24:56 +0200 Subject: [PATCH 024/319] Navigation: Scoring uses rectangle as clipped by parent. Fix selectable extending past parent column among other things (#323) --- imgui.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7e3412f6..59a34b67 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1911,10 +1911,11 @@ static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1) // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057 // FIXME-NAVIGATION: Pretty rough. // FIXME-NAVIGATION: May want to handle the degenerate case that we have commented out. -static bool NavScoreItem(const ImRect& cand) +static bool NavScoreItem(ImRect cand) { ImGuiContext& g = *GImGui; const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having lots of items with varied width) + cand.Clip(g.CurrentWindow->ClipRect); // Compute distance between boxes // FIXME-NAVIGATION: Introducing various biases toward typical imgui uses cases, but we don't have any rigorous proof of their effect now. @@ -1960,7 +1961,9 @@ static bool NavScoreItem(const ImRect& cand) if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) { char buf[128]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "db (%.0f,%.0f->%.1f) dc (%.0f,%.0f->%.1f) da (%.0f,%.0f->%.1f) quad %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[quadrant]); + ImFormatString(buf, IM_ARRAYSIZE(buf), "db (%.0f,%.0f->%.5f) dc (%.0f,%.0f->%.5f) da (%.0f,%.0f->%.5f) quad %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[quadrant]); + g.OverlayDrawList.AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); + g.OverlayDrawList.AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150)); g.OverlayDrawList.AddText(cand.Max, ~0U, buf); } #endif @@ -2052,7 +2055,7 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar { //if (!g.NavMoveRequest) g.NavMoveDir = ImGuiNavDir_E; // [DEBUG] Removing if (g.NavMoveRequest) above allows debug scoring of all visible items. const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; - if (NavScoreItem(nav_bb)) + if (NavScoreItem(nav_bb)) //if (g.NavMoveRequest) // [DEBUG] { g.NavMoveResultBestId = *id; g.NavMoveResultBestRefRectRel = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); From e74d96642f9f5b44d63114a3fbd96072860440fc Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 23 Jul 2016 19:42:19 +0200 Subject: [PATCH 025/319] Navigation: mouse/popup position gets clamped within visible display (#323) --- imgui.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 59a34b67..6ba30efc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2270,9 +2270,11 @@ int ImGui::GetFrameCount() static ImVec2 NavCalcPreferredMousePos() { ImGuiContext& g = *GImGui; - if (g.NavWindow) - return g.NavWindow->Pos + ImVec2(g.NavRefRectRel.Min.x + ImMin(g.Style.FramePadding.x*4, g.NavRefRectRel.GetWidth()), g.NavRefRectRel.Max.y - ImMin(g.Style.FramePadding.y, g.NavRefRectRel.GetHeight())); - return g.IO.MousePos; + if (!g.NavWindow) + return g.IO.MousePos; + ImVec2 p = g.NavWindow->Pos + ImVec2(g.NavRefRectRel.Min.x + ImMin(g.Style.FramePadding.x*4, g.NavRefRectRel.GetWidth()), g.NavRefRectRel.Max.y - ImMin(g.Style.FramePadding.y, g.NavRefRectRel.GetHeight())); + ImRect r = GetVisibleRect(); + return ImClamp(p, r.Min, r.Max); } static void NavUpdate() From afadc7cf591168beaa502c9fc78708c031870a3d Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 23 Jul 2016 20:05:17 +0200 Subject: [PATCH 026/319] Navigation: Scoring: Improved directional navigation. Not totally cancelling out dx. Better support for zero-spaced items. (#323) --- imgui.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6ba30efc..b989cfcc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1921,9 +1921,8 @@ static bool NavScoreItem(ImRect cand) // FIXME-NAVIGATION: Introducing various biases toward typical imgui uses cases, but we don't have any rigorous proof of their effect now. float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Clamp down on Y to keep using box-distance for vertically touching items - //dbx /= 2; dby *= 4 // Bias for dy - if (dby) - dbx = (dbx > 0.0f) ? +0.0f : -0.0f; + if (dby && dbx) + dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f); float dist_box = fabsf(dbx) + fabsf(dby); // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter) @@ -1957,7 +1956,6 @@ static bool NavScoreItem(ImRect cand) } #if 0 // [DEBUG] - //draw_list->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,200)); if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) { char buf[128]; @@ -2042,7 +2040,6 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar // We could early out with `if (is_clipped && !g.NavInitDefaultRequest) return false;` but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) // A more pragmatic solution for handling last lists is relying on the fact that they are likely evenly spread items (so that clipper can work) and we could nav at higher-level (apply index, etc.) // So eventually we would like to provide the user will the primitives to be able to implement that sort of customized/efficient navigation handling whenever necessary. - // FIXME-NAVIGATION if (id != NULL && g.NavWindow == window && g.IO.NavUsable) { if (g.NavInitDefaultRequest && window->DC.AllowNavDefaultFocus) @@ -2051,11 +2048,12 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar g.NavInitDefaultResultId = *id; } - if (g.NavMoveRequest && g.NavId != *id) + const bool DEBUG_NAV = false; // [DEBUG] Enable to test scoring on all items. + if ((g.NavMoveRequest || DEBUG_NAV) && g.NavId != *id) { - //if (!g.NavMoveRequest) g.NavMoveDir = ImGuiNavDir_E; // [DEBUG] Removing if (g.NavMoveRequest) above allows debug scoring of all visible items. + //if (DEBUG_NAV && !g.NavMoveRequest) g.NavMoveDir = ImGuiNavDir_N; const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; - if (NavScoreItem(nav_bb)) //if (g.NavMoveRequest) // [DEBUG] + if (NavScoreItem(nav_bb)) //if (!DEBUG || g.NavMoveRequest) { g.NavMoveResultBestId = *id; g.NavMoveResultBestRefRectRel = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); @@ -2457,9 +2455,12 @@ static void NavUpdate() // Reset search g.NavMoveResultBestId = 0; g.NavMoveResultBestDistAxial = g.NavMoveResultBestDistBox = g.NavMoveResultBestDistCenter = FLT_MAX; + + // 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(); - //g.OverlayDrawList.AddRect(g.NavRefRectScreen.Min, g.NavRefRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] + g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; + //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] } void ImGui::NewFrame() From 2f945635640db7031cf07402072384c86ce0dd6a Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 23 Jul 2016 20:09:41 +0200 Subject: [PATCH 027/319] Navigation: Collapsed window uses ImGuiCol_TitleBgActive when navigation is active (#323) --- imgui.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b989cfcc..3641e135 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4622,8 +4622,9 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us const float window_rounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding; if (window->Collapsed) { - // Draw title bar only - RenderFrame(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32(ImGuiCol_TitleBgCollapsed), true, window_rounding); + // Title bar only + const bool is_focused = g.FocusedWindow && window->RootNonPopupWindow == g.FocusedWindow->RootNonPopupWindow && !g.NavDisableHighlight; + RenderFrame(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32(is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed), true, window_rounding); } else { @@ -4704,8 +4705,9 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, ColorConvertFloat4ToU32(bg_color), window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 15 : 4|8); // Title bar + const bool is_focused = g.FocusedWindow && window->RootNonPopupWindow == g.FocusedWindow->RootNonPopupWindow; if (!(flags & ImGuiWindowFlags_NoTitleBar)) - window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32((g.FocusedWindow && window->RootNonPopupWindow == g.FocusedWindow->RootNonPopupWindow) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, 1|2); + window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32(is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, 1|2); // Menu bar if (flags & ImGuiWindowFlags_MenuBar) From e9d8b48a4c233d3684f589018c830af6536338d6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 23 Jul 2016 20:30:44 +0200 Subject: [PATCH 028/319] Navigation: Allow to navigate out of text input with direction or NavCancel (#323) --- imgui.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3641e135..c5322576 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2337,6 +2337,7 @@ static void NavUpdate() { // Apply result from previous navigation directional move request IM_ASSERT(g.NavWindow); + ImGui::SetActiveID(0); g.NavId = g.NavWindow->NavLastId = g.NavMoveResultBestId; g.NavRefRectRel = g.NavMoveResultBestRefRectRel; g.NavMousePosDirty = true; @@ -2387,11 +2388,15 @@ static void NavUpdate() g.IO.NavActive = g.IO.NavUsable && g.NavId != 0 && !g.NavDisableHighlight; // Process NavCancel input (to close a popup, get back to parent, clear focus) - if (g.ActiveId == 0 && IsKeyPressedMap(ImGuiKey_NavCancel)) + if (IsKeyPressedMap(ImGuiKey_NavCancel)) { - // Close open popup or move back to parent window - if (g.OpenPopupStack.Size > 0) + if (g.ActiveId != 0) { + ImGui::SetActiveID(0); + } + else if (g.OpenPopupStack.Size > 0) + { + // Close open popup or move back to parent window ClosePopupToLevel(g.OpenPopupStack.Size - 1); } else @@ -6911,6 +6916,7 @@ bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen) SetActiveIDNoNav(g.ScalarAsInputTextId, window); + g.ActiveIdAllowNavMove = true; SetHoveredID(0); FocusableItemUnregister(window); @@ -8363,6 +8369,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 select_all = true; } SetActiveID(id, window); + g.ActiveIdAllowNavMove = true; FocusWindow(window); } else if (io.MouseClicked[0]) From 4bbdaa4e3c09688d58df472d76a91dcc5e2f90f3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 24 Jul 2016 11:56:36 +0200 Subject: [PATCH 029/319] Navigation: Moving NavInitWindow() around and making it static (#323) --- imgui.cpp | 36 ++++++++++++++++++------------------ imgui_internal.h | 1 - 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c5322576..8d51f5e7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2265,6 +2265,24 @@ int ImGui::GetFrameCount() return GImGui->FrameCount; } +// This needs to be called before we submit any widget (aka in or before Begin) +static void NavInitWindow(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastId == 0)) + { + g.NavId = window->NavLastId = 0; + g.NavInitDefaultRequest = true; + g.NavInitDefaultResultExplicit = false; + g.NavInitDefaultResultId = 0; + } + else + { + g.NavId = window->NavLastId; + } + g.NavWindow = window; +} + static ImVec2 NavCalcPreferredMousePos() { ImGuiContext& g = *GImGui; @@ -5059,24 +5077,6 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.Windows.push_back(window); } -// This needs to be called before we submit any widget (aka in or before Begin) -void ImGui::NavInitWindow(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastId == 0)) - { - g.NavId = window->NavLastId = 0; - g.NavInitDefaultRequest = true; - g.NavInitDefaultResultExplicit = false; - g.NavInitDefaultResultId = 0; - } - else - { - g.NavId = window->NavLastId; - } - g.NavWindow = window; -} - void ImGui::PushItemWidth(float item_width) { ImGuiWindow* window = GetCurrentWindow(); diff --git a/imgui_internal.h b/imgui_internal.h index 6180891d..3da73deb 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -773,7 +773,6 @@ namespace ImGui IMGUI_API void OpenPopupEx(const char* str_id, bool reopen_existing); - IMGUI_API void NavInitWindow(ImGuiWindow* window); IMGUI_API ImVec2 NavGetTweakDelta(); 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); } From ba43310df105bcca90573093312622aeba281404 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 24 Jul 2016 12:46:25 +0200 Subject: [PATCH 030/319] Navigation: Renaming, shortened some unnecessary long fields names (#323) --- imgui.cpp | 54 ++++++++++++++++++++++++------------------------ imgui_internal.h | 14 ++++++------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8d51f5e7..04684e9e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1971,21 +1971,21 @@ static bool NavScoreItem(ImRect cand) if (quadrant == g.NavMoveDir) { // Does it beat the current best candidate? - if (dist_box < g.NavMoveResultBestDistBox) + if (dist_box < g.NavMoveResultDistBox) { - g.NavMoveResultBestDistBox = dist_box; - g.NavMoveResultBestDistCenter = dist_center; + g.NavMoveResultDistBox = dist_box; + g.NavMoveResultDistCenter = dist_center; return true; } - if (dist_box == g.NavMoveResultBestDistBox) + if (dist_box == g.NavMoveResultDistBox) { // Try using distance between center points to break ties - if (dist_center < g.NavMoveResultBestDistCenter) + if (dist_center < g.NavMoveResultDistCenter) { - g.NavMoveResultBestDistCenter = dist_center; + g.NavMoveResultDistCenter = dist_center; new_best = true; } - else if (dist_center == g.NavMoveResultBestDistCenter) + else if (dist_center == g.NavMoveResultDistCenter) { // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" buttons // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), @@ -1999,10 +1999,10 @@ static bool NavScoreItem(ImRect cand) // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness) // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too. - if (g.NavMoveResultBestDistBox == FLT_MAX) - if (dist_axial < g.NavMoveResultBestDistAxial) // Check axial match + if (g.NavMoveResultDistBox == FLT_MAX) + if (dist_axial < g.NavMoveResultDistAxial) // Check axial match if ((g.NavMoveDir == ImGuiNavDir_W && dax < 0.0f) || (g.NavMoveDir == ImGuiNavDir_E && dax > 0.0f) || (g.NavMoveDir == ImGuiNavDir_N && day < 0.0f) || (g.NavMoveDir == ImGuiNavDir_S && day > 0.0f)) - g.NavMoveResultBestDistAxial = dist_axial, new_best = true; + g.NavMoveResultDistAxial = dist_axial, new_best = true; return new_best; } @@ -2055,9 +2055,9 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; if (NavScoreItem(nav_bb)) //if (!DEBUG || g.NavMoveRequest) { - g.NavMoveResultBestId = *id; - g.NavMoveResultBestRefRectRel = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); - //g.OverlayDrawList.AddRectFilled(g.NavRefRectScreen.Min, g.NavRefRectScreen.Max+ImVec2(2,2), IM_COL32(255,255,0,255)); // [DEBUG] + g.NavMoveResultId = *id; + g.NavMoveResultRectRel = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); + //g.OverlayDrawList.AddRectFilled(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max+ImVec2(2,2), IM_COL32(255,255,0,255)); // [DEBUG] //g.OverlayDrawList.AddRectFilled(nav_bb.Min, nav_bb.Max, IM_COL32(255,0,255,100)); // [DEBUG] //g.OverlayDrawList.AddText(nav_bb.Min, ~0U, "new_best"); // [DEBUG] } @@ -2326,38 +2326,38 @@ static void NavUpdate() ImRect window_rect_rel(g.NavWindow->InnerRect.Min - g.NavWindow->Pos, g.NavWindow->InnerRect.Max - g.NavWindow->Pos); window_rect_rel.Expand(1.0f); //g.OverlayDrawList.AddRect(g.NavWindow->Pos + window_rect_rel.Min, g.NavWindow->Pos + window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG] - if (g.NavWindow && g.NavMoveResultBestId != 0 && !window_rect_rel.Contains(g.NavMoveResultBestRefRectRel)) + if (g.NavWindow && g.NavMoveResultId != 0 && !window_rect_rel.Contains(g.NavMoveResultRectRel)) { - if (g.NavWindow->ScrollbarX && g.NavMoveResultBestRefRectRel.Min.x < window_rect_rel.Min.x) + if (g.NavWindow->ScrollbarX && g.NavMoveResultRectRel.Min.x < window_rect_rel.Min.x) { - g.NavWindow->ScrollTarget.x = g.NavMoveResultBestRefRectRel.Min.x + g.NavWindow->Scroll.x - g.Style.ItemSpacing.x; + g.NavWindow->ScrollTarget.x = g.NavMoveResultRectRel.Min.x + g.NavWindow->Scroll.x - g.Style.ItemSpacing.x; g.NavWindow->ScrollTargetCenterRatio.x = 0.0f; } - else if (g.NavWindow->ScrollbarX && g.NavMoveResultBestRefRectRel.Max.x >= window_rect_rel.Max.x) + else if (g.NavWindow->ScrollbarX && g.NavMoveResultRectRel.Max.x >= window_rect_rel.Max.x) { - g.NavWindow->ScrollTarget.x = g.NavMoveResultBestRefRectRel.Max.x + g.NavWindow->Scroll.x + g.Style.ItemSpacing.x; + g.NavWindow->ScrollTarget.x = g.NavMoveResultRectRel.Max.x + g.NavWindow->Scroll.x + g.Style.ItemSpacing.x; g.NavWindow->ScrollTargetCenterRatio.x = 1.0f; } - if (g.NavMoveResultBestRefRectRel.Min.y < window_rect_rel.Min.y) + if (g.NavMoveResultRectRel.Min.y < window_rect_rel.Min.y) { - g.NavWindow->ScrollTarget.y = g.NavMoveResultBestRefRectRel.Min.y + g.NavWindow->Scroll.y - g.Style.ItemSpacing.y; + g.NavWindow->ScrollTarget.y = g.NavMoveResultRectRel.Min.y + g.NavWindow->Scroll.y - g.Style.ItemSpacing.y; g.NavWindow->ScrollTargetCenterRatio.y = 0.0f; } - else if (g.NavMoveResultBestRefRectRel.Max.y >= window_rect_rel.Max.y) + else if (g.NavMoveResultRectRel.Max.y >= window_rect_rel.Max.y) { - g.NavWindow->ScrollTarget.y = g.NavMoveResultBestRefRectRel.Max.y + g.NavWindow->Scroll.y + g.Style.ItemSpacing.y; + g.NavWindow->ScrollTarget.y = g.NavMoveResultRectRel.Max.y + g.NavWindow->Scroll.y + g.Style.ItemSpacing.y; g.NavWindow->ScrollTargetCenterRatio.y = 1.0f; } } } - if (g.NavMoveRequest && g.NavMoveResultBestId != 0) + if (g.NavMoveRequest && g.NavMoveResultId != 0) { // Apply result from previous navigation directional move request IM_ASSERT(g.NavWindow); ImGui::SetActiveID(0); - g.NavId = g.NavWindow->NavLastId = g.NavMoveResultBestId; - g.NavRefRectRel = g.NavMoveResultBestRefRectRel; + g.NavId = g.NavWindow->NavLastId = g.NavMoveResultId; + g.NavRefRectRel = g.NavMoveResultRectRel; g.NavMousePosDirty = true; g.NavDisableHighlight = false; g.NavDisableMouseHover = true; @@ -2476,8 +2476,8 @@ static void NavUpdate() } // Reset search - g.NavMoveResultBestId = 0; - g.NavMoveResultBestDistAxial = g.NavMoveResultBestDistBox = g.NavMoveResultBestDistCenter = FLT_MAX; + g.NavMoveResultId = 0; + g.NavMoveResultDistAxial = g.NavMoveResultDistBox = g.NavMoveResultDistCenter = FLT_MAX; // 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(); diff --git a/imgui_internal.h b/imgui_internal.h index 3da73deb..fc6ffbd1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -420,11 +420,11 @@ struct ImGuiContext bool NavInitDefaultResultExplicit; // Whether the result was explicitly requested with SetItemDefaultFocus() bool NavMoveRequest; // Move request for this frame ImGuiNavDir NavMoveDir; // West/East/North/South - ImGuiID NavMoveResultBestId; // Best move request candidate - float NavMoveResultBestDistBox; // Best move request candidate box distance to current NavId - float NavMoveResultBestDistCenter; // Best move request candidate center distance to current NavId - float NavMoveResultBestDistAxial; - ImRect NavMoveResultBestRefRectRel; // Best move request candidate bounding box in window space + ImGuiID NavMoveResultId; // Best move request candidate + float NavMoveResultDistBox; // Best move request candidate box distance to current NavId + float NavMoveResultDistCenter; // Best move request candidate center distance to current NavId + float NavMoveResultDistAxial; + ImRect NavMoveResultRectRel; // Best move request candidate bounding box in window relative space // Storage for SetNexWindow** and SetNextTreeNode*** functions ImVec2 SetNextWindowPosVal; @@ -524,8 +524,8 @@ struct ImGuiContext NavInitDefaultResultExplicit = false; NavMoveRequest = false; NavMoveDir = ImGuiNavDir_None; - NavMoveResultBestId = 0; - NavMoveResultBestDistBox = NavMoveResultBestDistCenter = NavMoveResultBestDistAxial = 0.0f; + NavMoveResultId = 0; + NavMoveResultDistBox = NavMoveResultDistCenter = NavMoveResultDistAxial = 0.0f; SetNextWindowPosVal = ImVec2(0.0f, 0.0f); SetNextWindowSizeVal = ImVec2(0.0f, 0.0f); From 5cac4926c82a41253bd3540ce1bb4f50b0cfc146 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 24 Jul 2016 13:56:31 +0200 Subject: [PATCH 031/319] Fixed display of TitleBgActive color on window before a Modal window + modal window availability for navigation (#323) --- imgui.cpp | 4 ++-- imgui_internal.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 04684e9e..37282b2d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4412,11 +4412,11 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us if (!(g.CurrentWindowStack[root_idx]->Flags & ImGuiWindowFlags_ChildWindow)) break; for (root_non_popup_idx = root_idx; root_non_popup_idx > 0; root_non_popup_idx--) - if (!(g.CurrentWindowStack[root_non_popup_idx]->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) + if (!(g.CurrentWindowStack[root_non_popup_idx]->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) || (g.CurrentWindowStack[root_non_popup_idx]->Flags & ImGuiWindowFlags_Modal)) break; window->ParentWindow = parent_window; window->RootWindow = g.CurrentWindowStack[root_idx]; - window->RootNonPopupWindow = g.CurrentWindowStack[root_non_popup_idx]; // Used e.g. to keep displaying TitleBgActive color when menu is active, and for NavWindowing + window->RootNonPopupWindow = g.CurrentWindowStack[root_non_popup_idx]; // Used to display TitleBgActive color and for selecting which window to use for NavWindowing // When reusing window again multiple times a frame, just append content (don't need to setup again) if (first_begin_of_the_frame) diff --git a/imgui_internal.h b/imgui_internal.h index fc6ffbd1..59381135 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -708,7 +708,7 @@ struct IMGUI_API ImGuiWindow float FontWindowScale; // Scale multiplier per-window ImDrawList* DrawList; ImGuiWindow* RootWindow; // If we are a child window, this is pointing to the first non-child parent window. Else point to ourself. - ImGuiWindow* RootNonPopupWindow; // If we are a child window, this is pointing to the first non-child non-popup parent window. Else point to ourself. + ImGuiWindow* RootNonPopupWindow; // Used to display TitleBgActive color and for selecting which window to use for NavWindowing ImGuiWindow* ParentWindow; // Immediate parent in the window stack *regardless* of whether this window is a child window or not) // Navigation / Focus From 49ec65b1b55ebb3cacacfc1a67701a250b9b0364 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 24 Jul 2016 14:00:29 +0200 Subject: [PATCH 032/319] Navigation: can't manually focus out of a modal window with controller (#323) --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 37282b2d..dffc3725 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2370,9 +2370,9 @@ static void NavUpdate() { // FIXME-NAVIGATION: Need to clarify input semantic, naming is misleading/incorrect here. int focus_change_dir = IsKeyPressedMap(ImGuiKey_NavTweakFaster, true) ? -1 : IsKeyPressedMap(ImGuiKey_NavTweakSlower, true) ? +1 : 0; - if (focus_change_dir != 0) + if (focus_change_dir != 0 && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)) { - // FIXME-NAVIGATION FIXME-OPT: This is absolutely hideous and shouldn't stay. Pressed we should maintain a intrusive linked-list of visible windows. + // FIXME-NAVIGATION FIXME-OPT: Code is absolutely hideous. Perhaps we should maintain a intrusive linked-list of visible windows. int i_current = -1; for (int i = g.Windows.Size-1; i >= 0 && i_current == -1; i--) if (g.Windows[i] == g.NavWindowingTarget) From a424d6f805f474e5faaf85eaae9e7a061c2063b7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 24 Jul 2016 14:08:50 +0200 Subject: [PATCH 033/319] Navigation: Fixed missing initialization (had no side-effect, would be set in Begin) (#323) --- imgui_internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_internal.h b/imgui_internal.h index 59381135..7f6de494 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -640,7 +640,7 @@ struct IMGUI_API ImGuiDrawContext LayoutType = ImGuiLayoutType_Vertical; ItemWidth = 0.0f; ButtonRepeat = false; - AllowKeyboardFocus = true; + AllowKeyboardFocus = AllowNavDefaultFocus = true; TextWrapPos = -1.0f; ColorEditMode = ImGuiColorEditMode_RGB; memset(StackSizesBackup, 0, sizeof(StackSizesBackup)); From 67feb5ac6c9c702ab576d00b20a370df44332074 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 24 Jul 2016 14:15:34 +0200 Subject: [PATCH 034/319] Navigation: comments + moving init block of code above in NavUpdate(), should have no side-effect (committed to simplify next commit) (#323) --- imgui.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index dffc3725..021ddd5f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2298,6 +2298,15 @@ static void NavUpdate() ImGuiContext& g = *GImGui; g.IO.WantMoveMouse = false; + // Process navigation init request (select first/default focus) + if (g.NavInitDefaultResultId != 0 && (!g.NavDisableHighlight || g.NavInitDefaultResultExplicit)) + { + // Apply result from previous navigation init request (typically select the first item, unless SetItemDefaultFocus() has been called) + IM_ASSERT(g.NavWindow); + g.NavId = g.NavWindow->NavLastId = g.NavInitDefaultResultId; + } + + // Apply application mouse position movement if (g.NavMousePosDirty && g.NavIdIsAlive) { // Set mouse position given our knowledge of the nav widget position from last frame @@ -2310,23 +2319,17 @@ static void NavUpdate() } g.NavIdIsAlive = false; g.NavTabbedId = 0; - - if (g.NavInitDefaultResultId != 0 && (!g.NavDisableHighlight || g.NavInitDefaultResultExplicit)) + + // Process navigation move request + if (g.NavMoveRequest && g.NavMoveResultId != 0) { - // Apply result from previous navigation init request (typically select the first item, unless SetItemDefaultFocus() has been called) IM_ASSERT(g.NavWindow); - g.NavId = g.NavWindow->NavLastId = g.NavInitDefaultResultId; - //if (g.NavInitDefaultResultExplicit) - // g.NavDisableHighlight = false; - } - if (g.NavMoveRequest) - { // 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); //g.OverlayDrawList.AddRect(g.NavWindow->Pos + window_rect_rel.Min, g.NavWindow->Pos + window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG] - if (g.NavWindow && g.NavMoveResultId != 0 && !window_rect_rel.Contains(g.NavMoveResultRectRel)) + if (!window_rect_rel.Contains(g.NavMoveResultRectRel)) { if (g.NavWindow->ScrollbarX && g.NavMoveResultRectRel.Min.x < window_rect_rel.Min.x) { @@ -2349,10 +2352,7 @@ static void NavUpdate() g.NavWindow->ScrollTargetCenterRatio.y = 1.0f; } } - } - if (g.NavMoveRequest && g.NavMoveResultId != 0) - { // Apply result from previous navigation directional move request IM_ASSERT(g.NavWindow); ImGui::SetActiveID(0); From 175f42420cccdc488682e3d608644b3dfe71c588 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 24 Jul 2016 14:41:27 +0200 Subject: [PATCH 035/319] Navigation: Can nav-out but not nav-in a window with ImGuiWindowFlags_NoNav flag (#323) --- imgui.cpp | 10 ++++------ imgui_internal.h | 1 + 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 021ddd5f..d37d3f02 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2368,6 +2368,7 @@ static void NavUpdate() g.NavWindowingTarget = g.NavWindow->RootNonPopupWindow; if (g.NavWindowingTarget) { + // Select window to focus // FIXME-NAVIGATION: Need to clarify input semantic, naming is misleading/incorrect here. int focus_change_dir = IsKeyPressedMap(ImGuiKey_NavTweakFaster, true) ? -1 : IsKeyPressedMap(ImGuiKey_NavTweakSlower, true) ? +1 : 0; if (focus_change_dir != 0 && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)) @@ -2379,16 +2380,13 @@ static void NavUpdate() i_current = i; int i_target = -1; for (int i = i_current+focus_change_dir; i >= 0 && i < g.Windows.Size && i_target == -1; i += focus_change_dir) - if (g.Windows[i]->Active && g.Windows[i] == g.Windows[i]->RootNonPopupWindow) + if (g.Windows[i]->IsNavigableTo()) i_target = i; for (int i = (focus_change_dir < 0) ? (g.Windows.Size-1) : 0; i >= 0 && i < g.Windows.Size && i_target == -1 && i_target != i_current; i += focus_change_dir) - if (g.Windows[i]->Active && g.Windows[i] == g.Windows[i]->RootNonPopupWindow) + if (g.Windows[i]->IsNavigableTo()) i_target = i; - if (i_target != -1) - { - IM_ASSERT(i_target != i_current); + if (i_target != -1 && i_target != i_current) // i_target might be == i_current in rare situation where we only have 1 navigable window g.NavWindowingTarget = g.Windows[i_target]; - } } // Apply actual focus only when leaving NavWindowing mode (until then the window was merely rendered front-most) diff --git a/imgui_internal.h b/imgui_internal.h index 7f6de494..084196dc 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -735,6 +735,7 @@ public: ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; } ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } + bool IsNavigableTo() const { return Active && this == this->RootNonPopupWindow && (!(Flags & ImGuiWindowFlags_NoNav) || this == GImGui->FocusedWindow); } }; //----------------------------------------------------------------------------- From c3aa36d9ab6f7834ab0d197ff31920648e53652c Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 24 Jul 2016 14:49:16 +0200 Subject: [PATCH 036/319] Navigation: Renamed ImGuiKey_NavWindowing to ImGuiKey_NavMenu (#323) --- imgui.cpp | 4 ++-- imgui.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d37d3f02..aec8dac5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2364,7 +2364,7 @@ static void NavUpdate() } // Navigation windowing mode (change focus, move/resize window) - if (!g.NavWindowingTarget && g.NavWindow && IsKeyPressedMap(ImGuiKey_NavWindowing, false)) + if (!g.NavWindowingTarget && g.NavWindow && IsKeyPressedMap(ImGuiKey_NavMenu, false)) g.NavWindowingTarget = g.NavWindow->RootNonPopupWindow; if (g.NavWindowingTarget) { @@ -2390,7 +2390,7 @@ static void NavUpdate() } // Apply actual focus only when leaving NavWindowing mode (until then the window was merely rendered front-most) - if (!IsKeyDownMap(ImGuiKey_NavWindowing)) + if (!IsKeyDownMap(ImGuiKey_NavMenu)) { if (g.NavWindowingTarget) if (!g.FocusedWindow || (g.NavWindowingTarget->RootNonPopupWindow != g.FocusedWindow->RootNonPopupWindow)) diff --git a/imgui.h b/imgui.h index a415deec..a8569c32 100644 --- a/imgui.h +++ b/imgui.h @@ -599,7 +599,7 @@ enum ImGuiKey_ ImGuiKey_NavActivate, // press button, tweak value // e.g. Space key, Circle button ImGuiKey_NavCancel, // close menu/popup/child, unselect // e.g. Escape key, Cross button ImGuiKey_NavInput, // text input // e.g. Enter key, Triangle button - ImGuiKey_NavWindowing, // change focus, move, resize // e.g. Square button + ImGuiKey_NavMenu, // access menu, focus, move, resize // e.g. Square button ImGuiKey_NavLeft, // e.g. Left arrow, D-Pad left ImGuiKey_NavRight, // e.g. Right arrow, D-Pad right ImGuiKey_NavUp, // e.g. Up arrow, D-Pad up From aeabda5a5feb2eab16e886b767c4a347b6df9b71 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 24 Jul 2016 14:56:14 +0200 Subject: [PATCH 037/319] Navigation: Tap NavMenu to access menu and close buttons, hold to focus/resize (#323) Introducing a concept of NavLayer to filter navigable items --- imgui.cpp | 168 +++++++++++++++++++++++++++++++++-------------- imgui_internal.h | 21 ++++-- 2 files changed, 133 insertions(+), 56 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index aec8dac5..8d187da9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1837,6 +1837,8 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window = NULL) g.NavDisableHighlight = true; g.NavId = id; if (window) + g.NavLayer = window->DC.NavLayerCurrent; + if (window && window->DC.NavLayerCurrent == 0) // (Assume that id correspond to the current NavLayer, which should be the case) window->NavLastId = id; } } @@ -1914,8 +1916,12 @@ static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1) static bool NavScoreItem(ImRect cand) { ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (g.NavLayer != window->DC.NavLayerCurrent) + return false; + const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having lots of items with varied width) - cand.Clip(g.CurrentWindow->ClipRect); + cand.Clip(window->ClipRect); // Compute distance between boxes // FIXME-NAVIGATION: Introducing various biases toward typical imgui uses cases, but we don't have any rigorous proof of their effect now. @@ -1952,7 +1958,7 @@ static bool NavScoreItem(ImRect cand) else { // Degenerate case: two overlapping buttons with same center, break ties using order - quadrant = (g.CurrentWindow->DC.LastItemId < g.NavId) ? ImGuiNavDir_W : ImGuiNavDir_E; + quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiNavDir_W : ImGuiNavDir_E; } #if 0 // [DEBUG] @@ -2030,8 +2036,9 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar window->DC.LastItemId = id ? *id : 0; window->DC.LastItemRect = bb; window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false; - if (id != NULL) window->DC.NavHasItemsNext = true; const bool is_clipped = IsClippedEx(bb, id, false); + if (id != NULL) + window->DC.NavLayerActiveFlagsNext |= (1 << window->DC.NavLayerCurrent); // Navigation processing runs prior to clipping early-out // (a) So that NavInitDefaultRequest can be honored, for newly opened windows to select a default widget @@ -2042,32 +2049,31 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar // So eventually we would like to provide the user will the primitives to be able to implement that sort of customized/efficient navigation handling whenever necessary. if (id != NULL && g.NavWindow == window && g.IO.NavUsable) { - if (g.NavInitDefaultRequest && window->DC.AllowNavDefaultFocus) + const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; + const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); + + if (g.NavInitDefaultRequest && g.NavLayer == window->DC.NavLayerCurrent && window->DC.AllowNavDefaultFocus) { g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = false; // Clear flag immediately, first item gets default, also simplify the if() in ItemAdd() g.NavInitDefaultResultId = *id; + g.NavInitDefaultResultRectRel = nav_bb_rel; } const bool DEBUG_NAV = false; // [DEBUG] Enable to test scoring on all items. if ((g.NavMoveRequest || DEBUG_NAV) && g.NavId != *id) { //if (DEBUG_NAV && !g.NavMoveRequest) g.NavMoveDir = ImGuiNavDir_N; - const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; if (NavScoreItem(nav_bb)) //if (!DEBUG || g.NavMoveRequest) { g.NavMoveResultId = *id; - g.NavMoveResultRectRel = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); - //g.OverlayDrawList.AddRectFilled(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max+ImVec2(2,2), IM_COL32(255,255,0,255)); // [DEBUG] - //g.OverlayDrawList.AddRectFilled(nav_bb.Min, nav_bb.Max, IM_COL32(255,0,255,100)); // [DEBUG] - //g.OverlayDrawList.AddText(nav_bb.Min, ~0U, "new_best"); // [DEBUG] + g.NavMoveResultRectRel = nav_bb_rel; } } // Update window-relative bounding box of navigated item if (g.NavId == *id) { - const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; - g.NavRefRectRel = ImRect(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); + g.NavRefRectRel = nav_bb_rel; g.NavIdIsAlive = true; g.NavIdTabCounter = window->FocusIdxTabCounter; } @@ -2266,21 +2272,24 @@ int ImGui::GetFrameCount() } // This needs to be called before we submit any widget (aka in or before Begin) -static void NavInitWindow(ImGuiWindow* window) +static void NavInitWindow(ImGuiWindow* window, bool force_reinit) { ImGuiContext& g = *GImGui; - if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastId == 0)) + IM_ASSERT(window == g.NavWindow); + if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastId == 0) || force_reinit) { - g.NavId = window->NavLastId = 0; + g.NavId = 0; + if (g.NavLayer == 0) + window->NavLastId = 0; g.NavInitDefaultRequest = true; - g.NavInitDefaultResultExplicit = false; g.NavInitDefaultResultId = 0; + g.NavInitDefaultResultExplicit = false; + g.NavInitDefaultResultRectRel = ImRect(); } else { g.NavId = window->NavLastId; } - g.NavWindow = window; } static ImVec2 NavCalcPreferredMousePos() @@ -2293,6 +2302,18 @@ static ImVec2 NavCalcPreferredMousePos() return ImClamp(p, r.Min, r.Max); } +static void SetNavIdMoveMouse(ImGuiID id, const ImRect& rect_rel) +{ + ImGuiContext& g = *GImGui; + g.NavId = id; + if (g.NavLayer == 0) + g.NavWindow->NavLastId = g.NavId; + g.NavRefRectRel = rect_rel; + g.NavMousePosDirty = true; + g.NavDisableHighlight = false; + g.NavDisableMouseHover = true; +} + static void NavUpdate() { ImGuiContext& g = *GImGui; @@ -2303,8 +2324,15 @@ static void NavUpdate() { // Apply result from previous navigation init request (typically select the first item, unless SetItemDefaultFocus() has been called) IM_ASSERT(g.NavWindow); - g.NavId = g.NavWindow->NavLastId = g.NavInitDefaultResultId; + g.NavId = g.NavInitDefaultResultId; + g.NavRefRectRel = g.NavInitDefaultResultRectRel; + g.NavMousePosDirty = true; + if (g.NavLayer == 0) + g.NavWindow->NavLastId = g.NavId; } + g.NavInitDefaultRequest = false; + g.NavInitDefaultResultExplicit = false; + g.NavInitDefaultResultId = 0; // Apply application mouse position movement if (g.NavMousePosDirty && g.NavIdIsAlive) @@ -2329,7 +2357,7 @@ static void NavUpdate() ImRect window_rect_rel(g.NavWindow->InnerRect.Min - g.NavWindow->Pos, g.NavWindow->InnerRect.Max - g.NavWindow->Pos); window_rect_rel.Expand(1.0f); //g.OverlayDrawList.AddRect(g.NavWindow->Pos + window_rect_rel.Min, g.NavWindow->Pos + window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG] - if (!window_rect_rel.Contains(g.NavMoveResultRectRel)) + if (g.NavLayer == 0 && !window_rect_rel.Contains(g.NavMoveResultRectRel)) { if (g.NavWindow->ScrollbarX && g.NavMoveResultRectRel.Min.x < window_rect_rel.Min.x) { @@ -2354,20 +2382,24 @@ static void NavUpdate() } // Apply result from previous navigation directional move request - IM_ASSERT(g.NavWindow); ImGui::SetActiveID(0); - g.NavId = g.NavWindow->NavLastId = g.NavMoveResultId; - g.NavRefRectRel = g.NavMoveResultRectRel; - g.NavMousePosDirty = true; - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; + SetNavIdMoveMouse(g.NavMoveResultId, g.NavMoveResultRectRel); } // Navigation windowing mode (change focus, move/resize window) if (!g.NavWindowingTarget && g.NavWindow && IsKeyPressedMap(ImGuiKey_NavMenu, false)) + { g.NavWindowingTarget = g.NavWindow->RootNonPopupWindow; + g.NavWindowingDisplayAlpha = 0.0f; + g.NavWindowingToggleLayer = true; + } if (g.NavWindowingTarget) { + // Visuals only appears after a brief time holding the button, so that a fast tap (to toggle NavLayer) doesn't add visual noise + const float pressed_duration = g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_NavMenu]]; + g.NavWindowingDisplayAlpha = ImMax(g.NavWindowingDisplayAlpha, ImSaturate((pressed_duration - 0.20f) / 0.05f)); + g.NavWindowingToggleLayer &= (g.NavWindowingDisplayAlpha < 1.0f); // Once button is held long enough we don't consider it a tag-to-toggle-layer press anymore. + // Select window to focus // FIXME-NAVIGATION: Need to clarify input semantic, naming is misleading/incorrect here. int focus_change_dir = IsKeyPressedMap(ImGuiKey_NavTweakFaster, true) ? -1 : IsKeyPressedMap(ImGuiKey_NavTweakSlower, true) ? +1 : 0; @@ -2387,14 +2419,35 @@ static void NavUpdate() i_target = i; if (i_target != -1 && i_target != i_current) // i_target might be == i_current in rare situation where we only have 1 navigable window g.NavWindowingTarget = g.Windows[i_target]; + g.NavWindowingToggleLayer = false; + g.NavWindowingDisplayAlpha = 1.0f; } - // Apply actual focus only when leaving NavWindowing mode (until then the window was merely rendered front-most) if (!IsKeyDownMap(ImGuiKey_NavMenu)) { - if (g.NavWindowingTarget) - if (!g.FocusedWindow || (g.NavWindowingTarget->RootNonPopupWindow != g.FocusedWindow->RootNonPopupWindow)) - ImGui::FocusWindow(g.NavWindowingTarget->RootNonPopupWindow); + // Apply actual focus only when releasing the NavMenu button (until then the window was merely rendered front-most) + if (g.NavWindowingTarget && (!g.FocusedWindow || g.NavWindowingTarget != g.FocusedWindow->RootNonPopupWindow)) + { + ImGui::FocusWindow(g.NavWindowingTarget); + g.NavDisableHighlight = false; + g.NavDisableMouseHover = true; + if (g.NavWindowingTarget->NavLastId == 0) + NavInitWindow(g.NavWindowingTarget, false); + } + + // Single press toggles NavLayer + if (g.NavWindowingToggleLayer) + { + if ((g.NavWindow->DC.NavLayerActiveFlags & (1<<1)) == 0 && (g.NavWindow->RootWindow->DC.NavLayerActiveFlags & (1<<1)) != 0) + ImGui::FocusWindow(g.NavWindow->RootWindow); + g.NavLayer = (g.NavWindow->DC.NavLayerActiveFlags & (1<<1)) ? (g.NavLayer ^ 1) : 0; + g.NavDisableHighlight = false; + g.NavDisableMouseHover = true; + if (g.NavLayer == 0 && g.NavWindow->NavLastId) + SetNavIdMoveMouse(g.NavWindow->NavLastId, ImRect()); + else + NavInitWindow(g.NavWindow, true); + } g.NavWindowingTarget = NULL; } } @@ -2415,6 +2468,14 @@ static void NavUpdate() // Close open popup or move back to parent window ClosePopupToLevel(g.OpenPopupStack.Size - 1); } + else if (g.NavLayer != 0) + { + g.NavLayer = 0; + if (g.NavWindow->NavLastId) + SetNavIdMoveMouse(g.NavWindow->NavLastId, ImRect()); + else + NavInitWindow(g.NavWindow, true); + } else { // Clear NavId for popups but keep it for regular child window so we can leave one and come back where we were @@ -2427,7 +2488,9 @@ static void NavUpdate() ImGuiWindow* child_window = g.FocusedWindow; ImGuiWindow* parent_window = g.FocusedWindow->ParentWindow; ImGui::FocusWindow(parent_window); - g.NavId = parent_window->NavLastId = parent_window->GetChildID(child_window); + g.NavId = parent_window->GetChildID(child_window); + if (g.NavLayer == 0) + parent_window->NavLastId = g.NavId; g.NavIdIsAlive = false; if (g.NavDisableMouseHover) g.NavMousePosDirty = true; @@ -2447,9 +2510,6 @@ static void NavUpdate() g.NavDisableHighlight = true; } g.NavMoveRequest = false; - g.NavInitDefaultRequest = false; - g.NavInitDefaultResultExplicit = false; - g.NavInitDefaultResultId = 0; // Initiate directional inputs request g.NavMoveDir = ImGuiNavDir_None; @@ -2467,7 +2527,7 @@ static void NavUpdate() } // Fallback manual-scroll with NavUp/NavDown when window has no navigable item - if (g.FocusedWindow && !g.FocusedWindow->DC.NavHasItems && g.FocusedWindow->DC.NavHasScroll && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNav) && g.NavMoveRequest && (g.NavMoveDir == ImGuiNavDir_N || g.NavMoveDir == ImGuiNavDir_S)) + if (g.FocusedWindow && !g.FocusedWindow->DC.NavLayerActiveFlags && g.FocusedWindow->DC.NavHasScroll && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNav) && g.NavMoveRequest && (g.NavMoveDir == ImGuiNavDir_N || g.NavMoveDir == ImGuiNavDir_S)) { 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_N) ? -1.0f : +1.0f) * scroll_speed)); @@ -3760,7 +3820,8 @@ void ImGui::SetItemDefaultFocus() { g.NavInitDefaultRequest = false; g.NavInitDefaultResultExplicit = true; - g.NavInitDefaultResultId = g.CurrentWindow->DC.LastItemId; + g.NavInitDefaultResultId = g.NavWindow->DC.LastItemId; + g.NavInitDefaultResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); if (!IsItemVisible()) SetScrollHere(); } @@ -4077,10 +4138,10 @@ bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, // Process navigation-in immediately so NavInit can run on first frame const ImGuiID id = parent_window->GetChildID(child_window); - if ((child_window->DC.NavHasItems || child_window->DC.NavHasScroll) && GImGui->NavActivateId == id) + if ((child_window->DC.NavLayerActiveFlags != 0 || child_window->DC.NavHasScroll) && GImGui->NavActivateId == id) { FocusWindow(child_window); - NavInitWindow(child_window); + NavInitWindow(child_window, false); SetActiveIDNoNav(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item } @@ -4118,8 +4179,8 @@ void ImGui::EndChild() ImGuiID id = parent_window->GetChildID(window); ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); ItemSize(sz); - ItemAdd(bb, (window->DC.NavHasItems || window->DC.NavHasScroll) ? &id : NULL); - if (window->DC.NavHasItems || window->DC.NavHasScroll) + ItemAdd(bb, (window->DC.NavLayerActiveFlags != 0 || window->DC.NavHasScroll) ? &id : NULL); + if (window->DC.NavLayerActiveFlags != 0 || window->DC.NavHasScroll) { //if (!window->DC.NavHasItems && window->DC.NavHasScroll && g.NavWindow == window) // As a special case, we render nav highlight of child when inside when only scrolling is possible //{ @@ -4634,8 +4695,8 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us { ImRect bb = window->Rect(); bb.Expand(g.FontSize); - window->DrawList->AddRectFilled(bb.Min, bb.Max, IM_COL32(255,255,255,30)/*ImGui::GetColorU32(ImGuiCol_HeaderHovered, 0.15f)*/, g.Style.WindowRounding); - window->DrawList->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_HeaderHovered), g.Style.WindowRounding); + window->DrawList->AddRectFilled(bb.Min, bb.Max, IM_COL32(255,255,255,(int)(30 * g.NavWindowingDisplayAlpha))/*ImGui::GetColorU32(ImGuiCol_HeaderHovered, 0.15f)*/, g.Style.WindowRounding); + window->DrawList->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_HeaderHovered, g.NavWindowingDisplayAlpha), g.Style.WindowRounding); } // Draw window + handle manual resize @@ -4682,6 +4743,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us else if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) { // FIXME-NAVIGATION: Should store and accumulate into a separate size buffer to handle sizing constraints properly + g.NavWindowingToggleLayer = false; size_target = window->SizeFull + nav_resize_delta; held = true; // For coloring } @@ -4781,8 +4843,8 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->DC.CursorMaxPos = window->DC.CursorStartPos; window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f; window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; - window->DC.NavHasItems = window->DC.NavHasItemsNext; - window->DC.NavHasItemsNext = false; + window->DC.NavLayerActiveFlags = window->DC.NavLayerActiveFlagsNext; + window->DC.NavLayerActiveFlagsNext = 0x00; window->DC.NavHasScroll = (GetScrollMaxY() > 0.0f); window->DC.MenuBarAppending = false; window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x); @@ -4791,8 +4853,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled - window->DC.AllowKeyboardFocus = true; - window->DC.AllowNavDefaultFocus = true; + window->DC.AllowKeyboardFocus = window->DC.AllowNavDefaultFocus = true; window->DC.ButtonRepeat = false; window->DC.ItemWidthStack.resize(0); window->DC.TextWrapPosStack.resize(0); @@ -4818,8 +4879,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us if (!(flags & (ImGuiWindowFlags_ChildWindow|ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup)) { FocusWindow(window); - IM_ASSERT(g.NavWindow == window); - NavInitWindow(window); + NavInitWindow(window, false); } // Title bar @@ -4829,8 +4889,10 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us { const float pad = 2.0f; const float rad = (window->TitleBarHeight() - pad*2.0f) * 0.5f; + window->DC.NavLayerCurrent++; if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad)) *p_open = false; + window->DC.NavLayerCurrent--; } const ImVec2 text_size = CalcTextSize(name, NULL, true); @@ -5041,8 +5103,9 @@ void ImGui::FocusWindow(ImGuiWindow* window) if (g.FocusedWindow != window) { - g.NavId = window ? window->NavLastId : 0; + g.NavId = window ? window->NavLastId : 0; // Restore NavId g.NavIdIsAlive = false; + g.NavLayer = 0; if (window && g.NavDisableMouseHover) g.NavMousePosDirty = true; g.NavRefRectRel.Min = g.NavRefRectRel.Max = window ? (window->DC.CursorStartPos - window->Pos) : ImVec2(0,0); @@ -6210,7 +6273,8 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius)); const bool backup_allow_nav_default_focus = window->DC.AllowNavDefaultFocus; // We could exposethis as a flag to ItemAdd() but it is such a unique case for now - window->DC.AllowNavDefaultFocus = false; + if (window->Flags & ImGuiWindowFlags_MenuBar) + window->DC.AllowNavDefaultFocus = false; const bool added = ItemAdd(bb, &id); // To allow navigation window->DC.AllowNavDefaultFocus = backup_allow_nav_default_focus; if (!added) @@ -9107,7 +9171,8 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi } else { - window->NavLastId = id; + if (window->DC.NavLayerCurrent == 0) + window->NavLastId = id; FocusWindow(window); OpenPopup(label); popup_open = true; @@ -9428,6 +9493,7 @@ bool ImGui::BeginMenuBar() PushClipRect(ImVec2(ImFloor(rect.Min.x+0.5f), ImFloor(rect.Min.y + window->BorderSize + 0.5f)), ImVec2(ImFloor(rect.Max.x+0.5f), ImFloor(rect.Max.y+0.5f)), false); window->DC.CursorPos = ImVec2(rect.Min.x + window->DC.MenuBarOffsetX, rect.Min.y);// + g.Style.FramePadding.y); window->DC.LayoutType = ImGuiLayoutType_Horizontal; + window->DC.NavLayerCurrent++; window->DC.MenuBarAppending = true; AlignFirstTextHeightToWidgets(); return true; @@ -9447,6 +9513,7 @@ void ImGui::EndMenuBar() window->DC.GroupStack.back().AdvanceCursor = false; EndGroup(); window->DC.LayoutType = ImGuiLayoutType_Vertical; + window->DC.NavLayerCurrent--; window->DC.MenuBarAppending = false; } @@ -10327,6 +10394,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3); ImGui::Text("%d allocations", ImGui::GetIO().MetricsAllocs); + ImGui::Text("sizeof(ImGuiContext) = %u, sizeof(ImGuiWindow) = %u", sizeof(ImGuiContext), sizeof(ImGuiWindow)); static bool show_clip_rects = true; ImGui::Checkbox("Show clipping rectangles when hovering a ImDrawCmd", &show_clip_rects); ImGui::Separator(); @@ -10407,7 +10475,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) NodeDrawList(window->DrawList, "DrawList"); ImGui::BulletText("Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y); - ImGui::BulletText("NavLastId: 0x%08x, NavHasItems: %d", window->NavLastId, window->DC.NavHasItems); + ImGui::BulletText("NavLastId: 0x%08x, NavLayerActiveFlags: %02X", window->NavLastId, window->DC.NavLayerActiveFlags); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); diff --git a/imgui_internal.h b/imgui_internal.h index 084196dc..67523c41 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -410,6 +410,9 @@ struct ImGuiContext ImRect NavRefRectRel, NavScoringRectScreen;// Reference rectangle, in window space. Modified rectangle for directional navigation scoring, in screen space. ImGuiWindow* NavWindow; // ImGuiWindow* NavWindowingTarget; + float NavWindowingDisplayAlpha; + bool NavWindowingToggleLayer; + int NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid bool NavMousePosDirty; @@ -417,6 +420,7 @@ struct ImGuiContext bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we disable mouse hovering until mouse is touched again bool NavInitDefaultRequest; // Init request for appearing window to select first item ImGuiID NavInitDefaultResultId; + ImRect NavInitDefaultResultRectRel; bool NavInitDefaultResultExplicit; // Whether the result was explicitly requested with SetItemDefaultFocus() bool NavMoveRequest; // Move request for this frame ImGuiNavDir NavMoveDir; // West/East/North/South @@ -514,6 +518,8 @@ struct ImGuiContext NavRefRectRel = NavScoringRectScreen = ImRect(); NavWindow = NULL; NavWindowingTarget = NULL; + NavWindowingDisplayAlpha = 0.0f; + NavWindowingToggleLayer = false; NavIdTabCounter = INT_MAX; NavIdIsAlive = false; NavMousePosDirty = false; @@ -586,10 +592,11 @@ struct IMGUI_API ImGuiDrawContext int TreeDepth; ImGuiID LastItemId; ImRect LastItemRect; - bool LastItemHoveredAndUsable; // Item rectangle is hovered, and its window is currently interactable with (not blocked by a popup preventing access to the window) - bool LastItemHoveredRect; // Item rectangle is hovered, but its window may or not be currently interactable with (might be blocked by a popup preventing access to the window) - bool NavHasItems, NavHasItemsNext; // Set when has any navigatable item - bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) + bool LastItemHoveredAndUsable; // Item rectangle is hovered, and its window is currently interactable with (not blocked by a popup preventing access to the window) + bool LastItemHoveredRect; // Item rectangle is hovered, but its window may or not be currently interactable with (might be blocked by a popup preventing access to the window) + bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) + int NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) + int NavLayerActiveFlags, NavLayerActiveFlagsNext; // Which layer have been written to. bool MenuBarAppending; float MenuBarOffsetX; ImVector ChildWindows; @@ -633,7 +640,9 @@ struct IMGUI_API ImGuiDrawContext LastItemId = 0; LastItemRect = ImRect(0.0f,0.0f,0.0f,0.0f); LastItemHoveredAndUsable = LastItemHoveredRect = false; - NavHasItems = NavHasItemsNext = NavHasScroll = false; + NavHasScroll = false; + NavLayerActiveFlags = NavLayerActiveFlagsNext = 0x00; + NavLayerCurrent = 0; MenuBarAppending = false; MenuBarOffsetX = 0.0f; StateStorage = NULL; @@ -686,7 +695,7 @@ struct IMGUI_API ImGuiWindow bool SkipItems; // == Visible && !Collapsed int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) - ImGuiID NavLastId; // Last known NavId for this window + ImGuiID NavLastId; // Last known NavId for this window, for nav layer 0 only. int AutoFitFramesX, AutoFitFramesY; bool AutoFitOnlyGrows; int AutoPosLastDirection; From f34d7ea1993a5c46cdefc175c8e4fd17dcfdaf4d Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 29 Jul 2016 11:23:04 +0200 Subject: [PATCH 038/319] Fixed bad merge --- imgui.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2ad9b2e8..41642697 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -823,12 +823,6 @@ ImGuiIO::ImGuiIO() FontAllowUserScaling = false; DisplayFramebufferScale = ImVec2(1.0f, 1.0f); DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f); -#ifdef __APPLE__ - WordMovementUsesAltKey = true; // OS X style: Text editing cursor movement using Alt instead of Ctrl - ShortcutsUseSuperKey = true; // OS X style: Shortcuts using Cmd/Super instead of Ctrl - DoubleClickSelectsWord = true; // OS X style: Double click selects by word instead of selecting whole text - MultiSelectUsesSuperKey = true; // OS X style: Multi-selection in lists uses Cmd/Super instead of Ctrl -#endif // User functions RenderDrawListsFn = NULL; From 20a0fde0124c08a7ead82357c9346acec0bafda6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 10:02:46 +0200 Subject: [PATCH 039/319] Tidying up default clipboard handler for non Windows-OS --- imgui.cpp | 19 ++++++------------- imgui_internal.h | 3 +-- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 41642697..3064e3a6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2833,11 +2833,7 @@ void ImGui::Shutdown() g.RenderDrawLists[i].clear(); g.OverlayDrawList.ClearFreeMemory(); g.ColorEditModeStorage.Clear(); - if (g.PrivateClipboard) - { - ImGui::MemFree(g.PrivateClipboard); - g.PrivateClipboard = NULL; - } + g.PrivateClipboard.clear(); g.InputTextState.Text.clear(); g.InputTextState.InitialText.clear(); g.InputTextState.TempTextBuffer.clear(); @@ -10336,21 +10332,18 @@ static void SetClipboardTextFn_DefaultImpl(const char* text) // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers static const char* GetClipboardTextFn_DefaultImpl() { - return GImGui->PrivateClipboard; + ImGuiContext& g = *GImGui; + return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin(); } // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers static void SetClipboardTextFn_DefaultImpl(const char* text) { ImGuiContext& g = *GImGui; - if (g.PrivateClipboard) - { - ImGui::MemFree(g.PrivateClipboard); - g.PrivateClipboard = NULL; - } + g.PrivateClipboard.clear(); const char* text_end = text + strlen(text); - g.PrivateClipboard = (char*)ImGui::MemAlloc((size_t)(text_end - text) + 1); - memcpy(g.PrivateClipboard, text, (size_t)(text_end - text)); + g.PrivateClipboard.resize((size_t)(text_end - text) + 1); + memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text)); g.PrivateClipboard[(int)(text_end - text)] = 0; } diff --git a/imgui_internal.h b/imgui_internal.h index 67523c41..34cc0cb7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -467,7 +467,7 @@ struct ImGuiContext float DragSpeedScaleFast; ImVec2 ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? char Tooltip[1024]; - char* PrivateClipboard; // If no custom clipboard handler is defined + ImVector PrivateClipboard; // If no custom clipboard handler is defined ImVec2 OsImePosRequest, OsImePosSet; // Cursor position request & last passed to the OS Input Method Editor // Logging @@ -554,7 +554,6 @@ struct ImGuiContext DragSpeedScaleFast = 10.0f; ScrollbarClickDeltaToGrabCenter = ImVec2(0.0f, 0.0f); memset(Tooltip, 0, sizeof(Tooltip)); - PrivateClipboard = NULL; OsImePosRequest = OsImePosSet = ImVec2(-1.0f, -1.0f); ModalWindowDarkeningRatio = 0.0f; From 47358020963a10135ff6784402e5f28288b6f1f5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 10:26:57 +0200 Subject: [PATCH 040/319] Shallow tidying up ctrl+wheel scaling code in NewFrame() --- imgui.cpp | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3064e3a6..542a80c8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2733,23 +2733,20 @@ void ImGui::NewFrame() if (g.HoveredWindow && g.IO.MouseWheel != 0.0f && !g.HoveredWindow->Collapsed) { ImGuiWindow* window = g.HoveredWindow; - if (g.IO.KeyCtrl) + if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling) { - if (g.IO.FontAllowUserScaling) - { - // Zoom / Scale window - float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); - float scale = new_font_scale / window->FontWindowScale; - window->FontWindowScale = new_font_scale; + // Zoom / Scale window + const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); + const float scale = new_font_scale / window->FontWindowScale; + window->FontWindowScale = new_font_scale; - const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; - window->Pos += offset; - window->PosFloat += offset; - window->Size *= scale; - window->SizeFull *= scale; - } + const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; + window->Pos += offset; + window->PosFloat += offset; + window->Size *= scale; + window->SizeFull *= scale; } - else if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse)) + else if (!g.IO.KeyCtrl && !(window->Flags & ImGuiWindowFlags_NoScrollWithMouse)) { // Scroll const int scroll_lines = (window->Flags & ImGuiWindowFlags_ComboBox) ? 3 : 5; From b2aaab873d850e37baefb5b09fd610c1bd6fcd19 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 10:56:52 +0200 Subject: [PATCH 041/319] Nav: collapse button is interactve, allow collapsing, tidying up, resize speed takes account of framebuffer scale (#323) --- imgui.cpp | 69 +++++++++++++++++++++++++++++++----------------- imgui_internal.h | 2 ++ 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 542a80c8..61284071 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1718,14 +1718,13 @@ ImGuiWindow::ImGuiWindow(const char* name) Name = ImStrdup(name); ID = ImHash(name, 0); IDStack.push_back(ID); - MoveId = GetID("#MOVE"); - Flags = 0; IndexWithinParent = 0; PosFloat = Pos = ImVec2(0.0f, 0.0f); Size = SizeFull = ImVec2(0.0f, 0.0f); SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f); WindowPadding = ImVec2(0.0f, 0.0f); + MoveId = GetID("#MOVE"); Scroll = ImVec2(0.0f, 0.0f); ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); @@ -1735,6 +1734,7 @@ ImGuiWindow::ImGuiWindow(const char* name) Active = WasActive = false; Accessed = false; Collapsed = false; + CollapseToggleWanted = false; SkipItems = false; BeginCount = 0; PopupId = 0; @@ -2514,10 +2514,10 @@ static void NavUpdate() g.NavMoveDir = ImGuiNavDir_None; if (g.FocusedWindow && !g.NavWindowingTarget && (g.ActiveId == 0 || g.ActiveIdAllowNavMove) && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNav)) { - if (IsKeyPressedMap(ImGuiKey_NavLeft, true)) g.NavMoveDir = ImGuiNavDir_W; + if (IsKeyPressedMap(ImGuiKey_NavLeft, true)) g.NavMoveDir = ImGuiNavDir_W; if (IsKeyPressedMap(ImGuiKey_NavRight, true)) g.NavMoveDir = ImGuiNavDir_E; - if (IsKeyPressedMap(ImGuiKey_NavUp, true)) g.NavMoveDir = ImGuiNavDir_N; - if (IsKeyPressedMap(ImGuiKey_NavDown, true)) g.NavMoveDir = ImGuiNavDir_S; + if (IsKeyPressedMap(ImGuiKey_NavUp, true)) g.NavMoveDir = ImGuiNavDir_N; + if (IsKeyPressedMap(ImGuiKey_NavDown, true)) g.NavMoveDir = ImGuiNavDir_S; } if (g.NavMoveDir != ImGuiNavDir_None) { @@ -4501,7 +4501,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse)) { ImRect title_bar_rect = window->TitleBarRect(); - if (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0]) + if (window->CollapseToggleWanted || (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])) { window->Collapsed = !window->Collapsed; if (!(flags & ImGuiWindowFlags_NoSavedSettings)) @@ -4513,6 +4513,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us { window->Collapsed = false; } + window->CollapseToggleWanted = false; // SIZE @@ -4718,11 +4719,9 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us ImVec2 nav_resize_delta(0.0f, 0.0f); if (g.NavWindowingTarget == window) { - const float resize_speed = ImFloor(40 * g.FontSize * g.IO.DeltaTime); - if (IsKeyDownMap(ImGuiKey_NavLeft)) nav_resize_delta.x -= resize_speed; - if (IsKeyDownMap(ImGuiKey_NavRight)) nav_resize_delta.x += resize_speed; - if (IsKeyDownMap(ImGuiKey_NavUp)) nav_resize_delta.y -= resize_speed; - if (IsKeyDownMap(ImGuiKey_NavDown)) nav_resize_delta.y += resize_speed; + const float resize_speed = ImFloor(600 * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); + nav_resize_delta = NavGetMovingDir() * resize_speed; + held |= (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f); // For coloring } ImVec2 size_target(FLT_MAX,FLT_MAX); @@ -4737,7 +4736,6 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us // FIXME-NAVIGATION: Should store and accumulate into a separate size buffer to handle sizing constraints properly g.NavWindowingToggleLayer = false; size_target = window->SizeFull + nav_resize_delta; - held = true; // For coloring } else if (held) { @@ -4877,20 +4875,38 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us // Title bar if (!(flags & ImGuiWindowFlags_NoTitleBar)) { + // Close & collapse button are on layer 1 (same as menus) and don't default focus + const bool backup_allow_nav_default_focus = window->DC.AllowNavDefaultFocus; + if (window->Flags & ImGuiWindowFlags_MenuBar) + window->DC.AllowNavDefaultFocus = false; + window->DC.NavLayerCurrent++; + + // Close button if (p_open != NULL) { - const float pad = 2.0f; - const float rad = (window->TitleBarHeight() - pad*2.0f) * 0.5f; - window->DC.NavLayerCurrent++; - if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad)) + const float PAD = 2.0f; + const float rad = (window->TitleBarHeight() - PAD*2.0f) * 0.5f; + if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-PAD - rad, PAD + rad), rad)) *p_open = false; - window->DC.NavLayerCurrent--; } + // Collapse button const ImVec2 text_size = CalcTextSize(name, NULL, true); - if (!(flags & ImGuiWindowFlags_NoCollapse)) + if (!(flags & ImGuiWindowFlags_NoCollapse) && g.IO.NavUsable) + { + ImGuiID id = window->GetID("#COLLAPSE"); + ImRect bb(window->Pos + style.FramePadding + ImVec2(1,1), window->Pos + style.FramePadding + ImVec2(g.FontSize,g.FontSize) - ImVec2(1,1)); + ItemAdd(bb, &id); // To allow navigation + if (ButtonBehavior(bb, id, NULL, NULL)) + window->CollapseToggleWanted = true; // Defer collapsing to next frame as we are too far in the Begin() function + RenderNavHighlight(id, bb); RenderCollapseTriangle(window->Pos + style.FramePadding, !window->Collapsed, 1.0f, true); + } + window->DC.NavLayerCurrent--; + window->DC.AllowNavDefaultFocus = backup_allow_nav_default_focus; + + // Title text (FIXME: refactor text alignment facilities along with RenderText helpers) ImVec2 text_min = window->Pos + style.FramePadding; ImVec2 text_max = window->Pos + ImVec2(window->Size.x - style.FramePadding.x, style.FramePadding.y*2 + text_size.y); ImVec2 clip_max = ImVec2(window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x), text_max.y); // Match the size of CloseWindowButton() @@ -6264,12 +6280,7 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) ImGuiWindow* window = GetCurrentWindow(); const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius)); - const bool backup_allow_nav_default_focus = window->DC.AllowNavDefaultFocus; // We could exposethis as a flag to ItemAdd() but it is such a unique case for now - if (window->Flags & ImGuiWindowFlags_MenuBar) - window->DC.AllowNavDefaultFocus = false; - const bool added = ItemAdd(bb, &id); // To allow navigation - window->DC.AllowNavDefaultFocus = backup_allow_nav_default_focus; - if (!added) + if (!ItemAdd(bb, &id)) // To allow navigation return false; bool hovered, held; @@ -7015,6 +7026,16 @@ int ImGui::ParseFormatPrecision(const char* fmt, int default_precision) return precision; } +ImVec2 ImGui::NavGetMovingDir() +{ + 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; + return dir; +} + // Adjustment delta for slider/drag/etc. // FIXME-NAVIGATION: Accelerate over time? Expose more settings? Handle faster/slower modifiers here instead of widget level? ImVec2 ImGui::NavGetTweakDelta() diff --git a/imgui_internal.h b/imgui_internal.h index 34cc0cb7..eefda8c7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -691,6 +691,7 @@ struct IMGUI_API ImGuiWindow bool WasActive; bool Accessed; // Set to true when any widget access the current window bool Collapsed; // Set when collapsing window to become only title-bar + bool CollapseToggleWanted; bool SkipItems; // == Visible && !Collapsed int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) @@ -783,6 +784,7 @@ namespace ImGui IMGUI_API void OpenPopupEx(const char* str_id, bool reopen_existing); IMGUI_API ImVec2 NavGetTweakDelta(); + IMGUI_API ImVec2 NavGetMovingDir(); 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); } From b3ddae07d13807602b830696df7f09d9eb0e1899 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 11:11:17 +0200 Subject: [PATCH 042/319] Nav: not activating widgets with NavActivate when NavMenu is held (#323) --- imgui.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 61284071..3a8dc7c8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2501,8 +2501,8 @@ static void NavUpdate() } } - g.NavActivateId = (g.NavId && !g.NavDisableHighlight && g.ActiveId == 0 && IsKeyPressedMap(ImGuiKey_NavActivate)) ? g.NavId : 0; - g.NavInputId = (g.NavId && !g.NavDisableHighlight && g.ActiveId == 0 && IsKeyPressedMap(ImGuiKey_NavInput)) ? g.NavId : 0; + g.NavActivateId = (g.NavId && !g.NavDisableHighlight && !g.NavWindowingTarget && g.ActiveId == 0 && IsKeyPressedMap(ImGuiKey_NavActivate)) ? g.NavId : 0; + g.NavInputId = (g.NavId && !g.NavDisableHighlight && !g.NavWindowingTarget && g.ActiveId == 0 && IsKeyPressedMap(ImGuiKey_NavInput)) ? g.NavId : 0; if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNav)) { g.NavActivateId = g.NavInputId = 0; @@ -6157,15 +6157,15 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool { // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse hovered = true; - if (IsKeyDownMap(ImGuiKey_NavActivate)) + if (!g.NavWindowingTarget && IsKeyDownMap(ImGuiKey_NavActivate)) { // Set active id so it can be queried by user via IsItemActive(), etc. but don't react to it ourselves g.NavActivateId = g.NavId; SetActiveID(g.NavId, window); g.ActiveIdAllowNavMove = true; + if (IsKeyPressedMap(ImGuiKey_NavActivate, (flags & ImGuiButtonFlags_Repeat) != 0)) + pressed = true; } - if (IsKeyPressedMap(ImGuiKey_NavActivate, (flags & ImGuiButtonFlags_Repeat) != 0)) - pressed = true; } bool held = false; From cbe03e3108386c5b4f4c28b5399f5ab34f4ba797 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 11:22:18 +0200 Subject: [PATCH 043/319] Nav: CalcListClipping/ImGuiListClipper: fix to ensure we can navigate clipped lists (#323) --- imgui.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 3a8dc7c8..01aa4659 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3509,6 +3509,11 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items const ImVec2 pos = window->DC.CursorPos; int start = (int)((window->ClipRect.Min.y - pos.y) / items_height); int end = (int)((window->ClipRect.Max.y - pos.y) / items_height); + if (g.NavMoveRequest && g.NavMoveDir == ImGuiNavDir_N) // When performing a navigation request, ensure we have one item extra in the direction we are moving to + start--; + if (g.NavMoveRequest && g.NavMoveDir == ImGuiNavDir_S) + end++; + start = ImClamp(start, 0, items_count); end = ImClamp(end + 1, start, items_count); *out_items_display_start = start; From d9d6b0e629118aff7c11a78405de7b261c1d180a Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 13:45:46 +0200 Subject: [PATCH 044/319] Nav: Renamed private ImGuiNavDir_ enum to use left/right/up/down to be consistent with key enums (#323) --- imgui.cpp | 26 +++++++++++++------------- imgui_internal.h | 10 +++++----- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 01aa4659..08195ca0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1898,8 +1898,8 @@ void ImGui::ItemSize(const ImRect& bb, float text_offset_y) static ImGuiNavDir NavScoreItemGetQuadrant(float dx, float dy) { if (fabsf(dx) > fabsf(dy)) - return (dx > 0.0f) ? ImGuiNavDir_E : ImGuiNavDir_W; - return (dy > 0.0f) ? ImGuiNavDir_S : ImGuiNavDir_N; + return (dx > 0.0f) ? ImGuiNavDir_Right : ImGuiNavDir_Left; + return (dy > 0.0f) ? ImGuiNavDir_Down : ImGuiNavDir_Up; } static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1) @@ -1957,7 +1957,7 @@ static bool NavScoreItem(ImRect cand) else { // Degenerate case: two overlapping buttons with same center, break ties using order - quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiNavDir_W : ImGuiNavDir_E; + quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiNavDir_Left : ImGuiNavDir_Right; } #if 0 // [DEBUG] @@ -1995,7 +1995,7 @@ static bool NavScoreItem(ImRect cand) // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" buttons // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis. - if ((g.NavMoveDir >= ImGuiNavDir_N ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance + if (((g.NavMoveDir == ImGuiNavDir_Up || g.NavMoveDir == ImGuiNavDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance new_best = true; } } @@ -2006,7 +2006,7 @@ static bool NavScoreItem(ImRect cand) // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too. if (g.NavMoveResultDistBox == FLT_MAX) if (dist_axial < g.NavMoveResultDistAxial) // Check axial match - if ((g.NavMoveDir == ImGuiNavDir_W && dax < 0.0f) || (g.NavMoveDir == ImGuiNavDir_E && dax > 0.0f) || (g.NavMoveDir == ImGuiNavDir_N && day < 0.0f) || (g.NavMoveDir == ImGuiNavDir_S && day > 0.0f)) + if ((g.NavMoveDir == ImGuiNavDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiNavDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiNavDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiNavDir_Down && day > 0.0f)) g.NavMoveResultDistAxial = dist_axial, new_best = true; return new_best; @@ -2514,10 +2514,10 @@ static void NavUpdate() g.NavMoveDir = ImGuiNavDir_None; if (g.FocusedWindow && !g.NavWindowingTarget && (g.ActiveId == 0 || g.ActiveIdAllowNavMove) && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNav)) { - if (IsKeyPressedMap(ImGuiKey_NavLeft, true)) g.NavMoveDir = ImGuiNavDir_W; - if (IsKeyPressedMap(ImGuiKey_NavRight, true)) g.NavMoveDir = ImGuiNavDir_E; - if (IsKeyPressedMap(ImGuiKey_NavUp, true)) g.NavMoveDir = ImGuiNavDir_N; - if (IsKeyPressedMap(ImGuiKey_NavDown, true)) g.NavMoveDir = ImGuiNavDir_S; + if (IsKeyPressedMap(ImGuiKey_NavLeft, true)) g.NavMoveDir = ImGuiNavDir_Left; + if (IsKeyPressedMap(ImGuiKey_NavRight, true)) g.NavMoveDir = ImGuiNavDir_Right; + if (IsKeyPressedMap(ImGuiKey_NavUp, true)) g.NavMoveDir = ImGuiNavDir_Up; + if (IsKeyPressedMap(ImGuiKey_NavDown, true)) g.NavMoveDir = ImGuiNavDir_Down; } if (g.NavMoveDir != ImGuiNavDir_None) { @@ -2526,10 +2526,10 @@ static void NavUpdate() } // 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_N || g.NavMoveDir == ImGuiNavDir_S)) + 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)) { 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_N) ? -1.0f : +1.0f) * scroll_speed)); + SetWindowScrollY(g.FocusedWindow, ImFloor(g.FocusedWindow->Scroll.y + ((g.NavMoveDir == ImGuiNavDir_Up) ? -1.0f : +1.0f) * scroll_speed)); } // Reset search @@ -3509,9 +3509,9 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items const ImVec2 pos = window->DC.CursorPos; int start = (int)((window->ClipRect.Min.y - pos.y) / items_height); int end = (int)((window->ClipRect.Max.y - pos.y) / items_height); - if (g.NavMoveRequest && g.NavMoveDir == ImGuiNavDir_N) // When performing a navigation request, ensure we have one item extra in the direction we are moving to + if (g.NavMoveRequest && g.NavMoveDir == ImGuiNavDir_Up) // When performing a navigation request, ensure we have one item extra in the direction we are moving to start--; - if (g.NavMoveRequest && g.NavMoveDir == ImGuiNavDir_S) + if (g.NavMoveRequest && g.NavMoveDir == ImGuiNavDir_Down) end++; start = ImClamp(start, 0, items_count); diff --git a/imgui_internal.h b/imgui_internal.h index eefda8c7..3b955854 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -206,11 +206,11 @@ enum ImGuiInputSource enum ImGuiNavDir { - ImGuiNavDir_None = -1, - ImGuiNavDir_W, // Needs to be 0..3 (using arithmetic op in NavScoreItem()) - ImGuiNavDir_E, - ImGuiNavDir_N, - ImGuiNavDir_S + ImGuiNavDir_None = -1, + ImGuiNavDir_Left = 0, + ImGuiNavDir_Right = 1, + ImGuiNavDir_Up = 2, + ImGuiNavDir_Down = 3, }; // 2D axis aligned bounding-box From d6ce800a209152b00adfc0bfd15056a132095bf1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 14:31:34 +0200 Subject: [PATCH 045/319] Nav: ActiveIdAllowNavMove -> ActiveIdAllowNavDirFlags for more flexibility (nav up/down typically allowed on a single-line text input) (#323) --- imgui.cpp | 22 ++++++++++++---------- imgui_internal.h | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 08195ca0..bbf80352 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1824,7 +1824,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window = NULL) ImGuiContext& g = *GImGui; g.ActiveIdIsJustActivated = (g.ActiveId != id); g.ActiveId = id; - g.ActiveIdAllowNavMove = false; + g.ActiveIdAllowNavDirFlags = 0; g.ActiveIdAllowOverlap = false; g.ActiveIdWindow = window; if (id) @@ -1847,7 +1847,7 @@ void ImGui::SetActiveIDNoNav(ImGuiID id, ImGuiWindow* window) ImGuiContext& g = *GImGui; g.ActiveIdIsJustActivated = (g.ActiveId != id); g.ActiveId = id; - g.ActiveIdAllowNavMove = false; + g.ActiveIdAllowNavDirFlags = 0; g.ActiveIdAllowOverlap = false; g.ActiveIdWindow = window; g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; @@ -2511,13 +2511,14 @@ static void NavUpdate() g.NavMoveRequest = false; // Initiate directional inputs request + const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; g.NavMoveDir = ImGuiNavDir_None; - if (g.FocusedWindow && !g.NavWindowingTarget && (g.ActiveId == 0 || g.ActiveIdAllowNavMove) && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNav)) + if (g.FocusedWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNav)) { - if (IsKeyPressedMap(ImGuiKey_NavLeft, true)) g.NavMoveDir = ImGuiNavDir_Left; - if (IsKeyPressedMap(ImGuiKey_NavRight, true)) g.NavMoveDir = ImGuiNavDir_Right; - if (IsKeyPressedMap(ImGuiKey_NavUp, true)) g.NavMoveDir = ImGuiNavDir_Up; - if (IsKeyPressedMap(ImGuiKey_NavDown, true)) g.NavMoveDir = ImGuiNavDir_Down; + if ((allowed_dir_flags & (1< Date: Sat, 30 Jul 2016 14:38:33 +0200 Subject: [PATCH 046/319] ImRect: fixed Clip() function being the other way around (INTERNAL API BREAKING CHANGE- wasn't exposed, hoping nobody uses that) + actually fixes the function behavior. --- imgui.cpp | 15 ++++++++------- imgui_internal.h | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bbf80352..80bd9ec3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1920,7 +1920,7 @@ static bool NavScoreItem(ImRect cand) return false; const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having lots of items with varied width) - cand.Clip(window->ClipRect); + window->ClipRect.Clip(cand); // Compute distance between boxes // FIXME-NAVIGATION: Introducing various biases toward typical imgui uses cases, but we don't have any rigorous proof of their effect now. @@ -3555,7 +3555,7 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c // Clip ImRect rect_clipped(r_min, r_max); if (clip) - rect_clipped.Clip(window->ClipRect); + window->ClipRect.Clip(rect_clipped); // Expand for touch input const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); @@ -4927,7 +4927,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() window->WindowRectClipped = window->Rect(); - window->WindowRectClipped.Clip(window->ClipRect); + window->ClipRect.Clip(window->WindowRectClipped); // Pressing CTRL+C while holding on a window copy its content to the clipboard // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope. @@ -8857,10 +8857,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 { ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true); if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((unsigned short)' ') * 0.50f); // So we can see selected empty lines - ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn)); - rect.Clip(clip_rect); - if (rect.Overlaps(clip_rect)) - draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); + ImRect cursor_rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn)); + ImRect clip_rect_r(clip_rect); + clip_rect_r.Clip(cursor_rect); + if (cursor_rect.Overlaps(clip_rect_r)) + draw_window->DrawList->AddRectFilled(cursor_rect.Min, cursor_rect.Max, bg_color); } rect_pos.x = render_pos.x - render_scroll.x; rect_pos.y += g.FontSize; diff --git a/imgui_internal.h b/imgui_internal.h index e51a60b1..44ee941c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -241,7 +241,7 @@ struct IMGUI_API ImRect void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; } void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; } void Reduce(const ImVec2& amount) { Min.x += amount.x; Min.y += amount.y; Max.x -= amount.x; Max.y -= amount.y; } - void Clip(const ImRect& clip) { if (Min.x < clip.Min.x) Min.x = clip.Min.x; if (Min.y < clip.Min.y) Min.y = clip.Min.y; if (Max.x > clip.Max.x) Max.x = clip.Max.x; if (Max.y > clip.Max.y) Max.y = clip.Max.y; } + void Clip(ImRect& r) const { r.Min.x = ImClamp(r.Min.x, Min.x, Max.x); r.Min.y = ImClamp(r.Min.y, Min.y, Max.y); r.Max.x = ImClamp(r.Max.x, Min.x, Max.x); r.Max.y = ImClamp(r.Max.y, Min.y, Max.y); } void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; } ImVec2 GetClosestPoint(ImVec2 p, bool on_edge) const { From f2d1472481725d0a89e27be6f2bc0db32a92fccf Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 15:17:12 +0200 Subject: [PATCH 047/319] Nav: Fixed navigating outside of current scrolling bounds (bug from 0cc20fca831e60b4898cf66e3ac595d8bdebb830) (#323) + spaces --- imgui.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 80bd9ec3..b36ee915 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1920,7 +1920,18 @@ static bool NavScoreItem(ImRect cand) return false; const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having lots of items with varied width) - window->ClipRect.Clip(cand); + + // We perform scoring on items bounding box clipped by their parent window on the other axis (clipping on our movement axis would give us equal scores for all clipped items) + if (g.NavMoveDir == ImGuiNavDir_Left || g.NavMoveDir == ImGuiNavDir_Right) + { + cand.Min.y = ImClamp(cand.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y); + cand.Max.y = ImClamp(cand.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y); + } + else + { + cand.Min.x = ImClamp(cand.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x); + cand.Max.x = ImClamp(cand.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x); + } // Compute distance between boxes // FIXME-NAVIGATION: Introducing various biases toward typical imgui uses cases, but we don't have any rigorous proof of their effect now. @@ -6168,7 +6179,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // Set active id so it can be queried by user via IsItemActive(), etc. but don't react to it ourselves g.NavActivateId = g.NavId; SetActiveID(g.NavId, window); - g.ActiveIdAllowNavDirFlags = (1< Date: Sat, 30 Jul 2016 16:25:45 +0200 Subject: [PATCH 048/319] Clarifying that MovedWindow* apply to mouse moving only --- imgui.cpp | 4 ++-- imgui_internal.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b36ee915..8bee3569 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2655,8 +2655,8 @@ void ImGui::NewFrame() g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame)); - // Handle user moving window (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows. - if (g.MovedWindowMoveId && g.MovedWindowMoveId == g.ActiveId) + // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows. + if (g.MovedWindowMoveId && g.MovedWindowMoveId == g.ActiveId && g.ActiveIdSource == ImGuiInputSource_Mouse) { KeepAliveID(g.MovedWindowMoveId); IM_ASSERT(g.MovedWindow && g.MovedWindow->RootWindow); diff --git a/imgui_internal.h b/imgui_internal.h index 44ee941c..b78494c5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -393,7 +393,7 @@ struct ImGuiContext ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) ImGuiWindow* ActiveIdWindow; ImGuiInputSource ActiveIdSource; // Activating with mouse or nav (gamepad/keyboard) - ImGuiWindow* MovedWindow; // Track the child window we clicked on to move a window. + ImGuiWindow* MovedWindow; // Track the child window we clicked on to move a window (only apply to moving with mouse) ImGuiID MovedWindowMoveId; // == MovedWindow->RootWindow->MoveId ImVector Settings; // .ini Settings float SettingsDirtyTimer; // Save .ini Settings on disk when time reaches zero From 04157da2919b657aa77a6b07863ae2007ceefe5f Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 17:18:34 +0200 Subject: [PATCH 049/319] Nav: first committed pass for manual moving and manual scrolling (after a bunch of attempts) (#323) --- imgui.cpp | 92 ++++++++++++++++++++++++++++++++++++++++-------- imgui.h | 5 +++ imgui_demo.cpp | 2 +- imgui_internal.h | 3 +- 4 files changed, 86 insertions(+), 16 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8bee3569..45b27881 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -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(); diff --git a/imgui.h b/imgui.h index a0e6ea8c..19650ec2 100644 --- a/imgui.h +++ b/imgui.h @@ -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 }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index c17537a0..db58ac08 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -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; diff --git a/imgui_internal.h b/imgui_internal.h index b78494c5..1f08a176 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -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); } From 3672105b87d1cc4513f5968d863699b94ca852c7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 17:20:59 +0200 Subject: [PATCH 050/319] Tidying up --- imgui.cpp | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 45b27881..a3157ab2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -682,7 +682,7 @@ static ImGuiIniData* FindWindowSettings(const char* name); static ImGuiIniData* AddWindowSettings(const char* name); static void LoadSettings(); static void SaveSettings(); -static void MarkSettingsDirty(); +static void MarkSettingsDirty(ImGuiWindow* window); static void PushColumnClipRect(int column_index = -1); static ImRect GetVisibleRect(); @@ -2442,8 +2442,7 @@ static void NavUpdate() { 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(); + MarkSettingsDirty(g.NavWindowingTarget); } } @@ -2708,8 +2707,7 @@ void ImGui::NewFrame() if (!(g.MovedWindow->Flags & ImGuiWindowFlags_NoMove)) { g.MovedWindow->PosFloat += g.IO.MouseDelta; - if (!(g.MovedWindow->Flags & ImGuiWindowFlags_NoSavedSettings)) - MarkSettingsDirty(); + MarkSettingsDirty(g.MovedWindow); } FocusWindow(g.MovedWindow); } @@ -3018,11 +3016,12 @@ static void SaveSettings() fclose(f); } -static void MarkSettingsDirty() +static void MarkSettingsDirty(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - if (g.SettingsDirtyTimer <= 0.0f) - g.SettingsDirtyTimer = g.IO.IniSavingRate; + if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings)) + if (g.SettingsDirtyTimer <= 0.0f) + g.SettingsDirtyTimer = g.IO.IniSavingRate; } // FIXME: Add a more explicit sort order in the window structure. @@ -4563,8 +4562,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us if (window->CollapseToggleWanted || (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])) { window->Collapsed = !window->Collapsed; - if (!(flags & ImGuiWindowFlags_NoSavedSettings)) - MarkSettingsDirty(); + MarkSettingsDirty(window); FocusWindow(window); } } @@ -4639,8 +4637,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x; if (window->AutoFitFramesY > 0) window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y; - if (!(flags & ImGuiWindowFlags_NoSavedSettings)) - MarkSettingsDirty(); + MarkSettingsDirty(window); } } @@ -4805,8 +4802,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us if (size_target.x != FLT_MAX && size_target.y != FLT_MAX) { ApplySizeFullWithConstraint(window, size_target); - if (!(flags & ImGuiWindowFlags_NoSavedSettings)) - MarkSettingsDirty(); + MarkSettingsDirty(window); } resize_col = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); From 4c4d750caef80aa92780fac29419f16509f7c7ae Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 18:05:14 +0200 Subject: [PATCH 051/319] Nav: Fixed Collapse icon disappearing when Nav is disabled (since b2aaab873d850e37baefb5b09fd610c1bd6fcd19) (#323) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index a3157ab2..ec2a8137 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4947,7 +4947,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us // Collapse button const ImVec2 text_size = CalcTextSize(name, NULL, true); - if (!(flags & ImGuiWindowFlags_NoCollapse) && g.IO.NavUsable) + if (!(flags & ImGuiWindowFlags_NoCollapse)) { ImGuiID id = window->GetID("#COLLAPSE"); ImRect bb(window->Pos + style.FramePadding + ImVec2(1,1), window->Pos + style.FramePadding + ImVec2(g.FontSize,g.FontSize) - ImVec2(1,1)); From e2dd48ae65521db418757b103c61898cc3765920 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 18:06:05 +0200 Subject: [PATCH 052/319] Nav: Split _NoNav window flag into _NoNavInputs and _NoNavFocus (#323) --- imgui.cpp | 10 +++++----- imgui.h | 3 ++- imgui_demo.cpp | 4 ++-- imgui_internal.h | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ec2a8137..bc8da2f4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2476,7 +2476,7 @@ static void NavUpdate() } // Set output flags for user application - g.IO.NavUsable = g.FocusedWindow && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNav); + g.IO.NavUsable = g.FocusedWindow && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNavInputs); g.IO.NavActive = g.IO.NavUsable && g.NavId != 0 && !g.NavDisableHighlight; // Process NavCancel input (to close a popup, get back to parent, clear focus) @@ -2527,7 +2527,7 @@ static void NavUpdate() g.NavActivateId = (g.NavId && !g.NavDisableHighlight && !g.NavWindowingTarget && g.ActiveId == 0 && IsKeyPressedMap(ImGuiKey_NavActivate)) ? g.NavId : 0; g.NavInputId = (g.NavId && !g.NavDisableHighlight && !g.NavWindowingTarget && g.ActiveId == 0 && IsKeyPressedMap(ImGuiKey_NavInput)) ? g.NavId : 0; - if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNav)) + if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { g.NavActivateId = g.NavInputId = 0; g.NavDisableHighlight = true; @@ -2537,7 +2537,7 @@ static void NavUpdate() // Initiate directional inputs request const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; g.NavMoveDir = ImGuiNavDir_None; - if (g.FocusedWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNav)) + if (g.FocusedWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { if ((allowed_dir_flags & (1<Flags & ImGuiWindowFlags_NoNav)) + if (g.FocusedWindow && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { // 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. @@ -2808,7 +2808,7 @@ void ImGui::NewFrame() // Pressing TAB activate widget focus //// NB: Don't discard FocusedWindow if it isn't active, so that a window that go on/off programatically won't lose its keyboard focus. // [2016/07/17] That comment was made invalid by 19d02becef94e8e0f1d432a8bd55cd783876583c - if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Active && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNav) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false)) + if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Active && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false)) if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) g.FocusedWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); else diff --git a/imgui.h b/imgui.h index 19650ec2..2f80b5b4 100644 --- a/imgui.h +++ b/imgui.h @@ -509,7 +509,8 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14, // Always show vertical scrollbar (even if ContentSize.y < Size.y) ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, // Always show horizontal scrollbar (even if ContentSize.x < Size.x) ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) - ImGuiWindowFlags_NoNav = 1 << 17, // No directional gamepad/keyboard navigation + ImGuiWindowFlags_NoNavFocus = 1 << 17, // No focusing of this window with gamepad/keyboard navigation + ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window // [Internal] ImGuiWindowFlags_ChildWindow = 1 << 20, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_ChildWindowAutoFitX = 1 << 21, // Don't use! For internal use by BeginChild() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index db58ac08..a6cd92d0 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -164,7 +164,7 @@ void ImGui::ShowTestWindow(bool* p_open) if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar; if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar; - if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; + if (no_nav) window_flags |= ImGuiWindowFlags_NoNavInputs; ImGui::SetNextWindowSize(ImVec2(550,680), ImGuiSetCond_FirstUseEver); if (!ImGui::Begin("ImGui Demo", p_open, window_flags)) { @@ -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|ImGuiWindowFlags_NoNav)) + if (!ImGui::Begin("Example: Fixed Overlay", p_open, ImVec2(0,0), 0.3f, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_NoFocusOnAppearing|ImGuiWindowFlags_NoNavFocus|ImGuiWindowFlags_NoNavInputs)) { ImGui::End(); return; diff --git a/imgui_internal.h b/imgui_internal.h index 1f08a176..fa0d8c2b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -745,7 +745,7 @@ public: ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; } ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } - bool IsNavigableTo() const { return Active && this == this->RootNonPopupWindow && (!(Flags & ImGuiWindowFlags_NoNav) || this == GImGui->FocusedWindow); } + bool IsNavigableTo() const { return Active && this == this->RootNonPopupWindow && (!(Flags & ImGuiWindowFlags_NoNavFocus) || this == GImGui->FocusedWindow); } }; //----------------------------------------------------------------------------- From 8d0186c82b40d867a10a0b2a658b4d443c22a82c Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 19:08:20 +0200 Subject: [PATCH 053/319] Nav: programmatic call to SetKeyboardFocusHere() doesn't quality as a nav input (doesn't position mouse) (#323) --- imgui.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bc8da2f4..635a1516 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2149,10 +2149,7 @@ bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent) - { - g.NavTabbedId = id; return true; - } if (allow_keyboard_focus && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent) { g.NavTabbedId = id; From cc66731c39a9ba1c0fc22c298a88db86a84804a2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 19:15:12 +0200 Subject: [PATCH 054/319] Nav: Calling SetItemDefaultFocus() doesn't make mouse cursor dirty if nav highlight is off (#323) --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 635a1516..81f1933e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2062,7 +2062,6 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar { const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); - if (g.NavInitDefaultRequest && g.NavLayer == window->DC.NavLayerCurrent && window->DC.AllowNavDefaultFocus) { g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = false; // Clear flag immediately, first item gets default, also simplify the if() in ItemAdd() @@ -2334,7 +2333,8 @@ static void NavUpdate() IM_ASSERT(g.NavWindow); g.NavId = g.NavInitDefaultResultId; g.NavRefRectRel = g.NavInitDefaultResultRectRel; - g.NavMousePosDirty = true; + if (!g.NavDisableHighlight) + g.NavMousePosDirty = true; if (g.NavLayer == 0) g.NavWindow->NavLastId = g.NavId; } From 3cc1419df34a12a9aef37d074f434fd20d3b1aea Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 19:16:41 +0200 Subject: [PATCH 055/319] Nav: making io.NavActive more suitable for end-user detecting if they should pass inputs to game (#323) --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 81f1933e..1e1be8d0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2474,7 +2474,7 @@ static void NavUpdate() // Set output flags for user application g.IO.NavUsable = g.FocusedWindow && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNavInputs); - g.IO.NavActive = g.IO.NavUsable && g.NavId != 0 && !g.NavDisableHighlight; + g.IO.NavActive = (g.IO.NavUsable && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavMoveRequest || g.NavInitDefaultRequest; // Process NavCancel input (to close a popup, get back to parent, clear focus) if (IsKeyPressedMap(ImGuiKey_NavCancel)) @@ -10598,6 +10598,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("ActiveId: 0x%08X/0x%08X, ActiveIdWindow: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); ImGui::Text("NavId: 0x%08X, NavWindow: '%s'", g.NavId, g.NavWindow ? g.NavWindow->Name : "NULL"); ImGui::Text("NavRefRectRel: (%.1f,%.1f)(%.1f,%.1f)", g.NavRefRectRel.Min.x, g.NavRefRectRel.Min.y, g.NavRefRectRel.Max.x, g.NavRefRectRel.Max.y); + ImGui::Text("NavUsable: %d, NavActive: %d", g.IO.NavUsable, g.IO.NavActive); ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); ImGui::TreePop(); } From d88dcc26cc42146f72ce4259950341a14e072756 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 19:17:26 +0200 Subject: [PATCH 056/319] Demo: console text input keeps focus in a less harsher and more navigation friendly way (#323) --- imgui_demo.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index a6cd92d0..7ec06dac 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2159,6 +2159,7 @@ struct ExampleAppConsole ImGui::Separator(); // Command-line + bool reclaim_focus = false; if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), ImGuiInputTextFlags_EnterReturnsTrue|ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_CallbackHistory, &TextEditCallbackStub, (void*)this)) { char* input_end = InputBuf+strlen(InputBuf); @@ -2166,10 +2167,12 @@ struct ExampleAppConsole if (InputBuf[0]) ExecCommand(InputBuf); strcpy(InputBuf, ""); + reclaim_focus = true; } - // Demonstrate keeping auto focus on the input box - if (ImGui::IsItemHovered() || (ImGui::IsRootWindowOrAnyChildFocused() && !ImGui::IsAnyItemActive() && !ImGui::IsMouseClicked(0))) + // Demonstrate keeping focus on the input box + ImGui::SetItemDefaultFocus(); + if (ImGui::IsItemHovered() || reclaim_focus) ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget ImGui::End(); From 79e7ece259a2eb62feb7b4ccced33cc9b1462fb0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 23:17:11 +0200 Subject: [PATCH 057/319] Nav/Examples: honoring the io.WantMoveMouse flag in most common examples (#323) Missing support Vulkan (#549), Apple (#575, #247), SDL (#58, #356), Allegro, Marmalade (#368, #375) --- examples/README.txt | 1 + examples/directx10_example/imgui_impl_dx10.cpp | 8 ++++++++ examples/directx11_example/imgui_impl_dx11.cpp | 8 ++++++++ examples/directx9_example/imgui_impl_dx9.cpp | 8 ++++++++ examples/opengl3_example/imgui_impl_glfw_gl3.cpp | 13 ++++++++++--- examples/opengl_example/imgui_impl_glfw.cpp | 13 ++++++++++--- 6 files changed, 45 insertions(+), 6 deletions(-) diff --git a/examples/README.txt b/examples/README.txt index df2e86da..b58f4343 100644 --- a/examples/README.txt +++ b/examples/README.txt @@ -84,3 +84,4 @@ vulkan_example/ Vulkan example. This is quite long and tedious, because: Vulkan. +TODO: Apple, SDL GL/GL3, Allegro, Marmalade, Vulkan examples do not honor the io.WantMoveMouse flag. diff --git a/examples/directx10_example/imgui_impl_dx10.cpp b/examples/directx10_example/imgui_impl_dx10.cpp index bccee87e..cc664be3 100644 --- a/examples/directx10_example/imgui_impl_dx10.cpp +++ b/examples/directx10_example/imgui_impl_dx10.cpp @@ -572,6 +572,14 @@ void ImGui_ImplDX10_NewFrame() // io.MouseDown : filled by WM_*BUTTON* events // io.MouseWheel : filled by WM_MOUSEWHEEL events + // Set OS mouse position if requested last frame by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) + if (io.WantMoveMouse) + { + POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; + ClientToScreen(g_hWnd, &pos); + SetCursorPos(pos.x, pos.y); + } + // Hide OS mouse cursor if ImGui is drawing it SetCursor(io.MouseDrawCursor ? NULL : LoadCursor(NULL, IDC_ARROW)); diff --git a/examples/directx11_example/imgui_impl_dx11.cpp b/examples/directx11_example/imgui_impl_dx11.cpp index 11f66f0e..d77d5c3f 100644 --- a/examples/directx11_example/imgui_impl_dx11.cpp +++ b/examples/directx11_example/imgui_impl_dx11.cpp @@ -575,6 +575,14 @@ void ImGui_ImplDX11_NewFrame() // io.MouseDown : filled by WM_*BUTTON* events // io.MouseWheel : filled by WM_MOUSEWHEEL events + // Set OS mouse position if requested last frame by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) + if (io.WantMoveMouse) + { + POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; + ClientToScreen(g_hWnd, &pos); + SetCursorPos(pos.x, pos.y); + } + // Hide OS mouse cursor if ImGui is drawing it SetCursor(io.MouseDrawCursor ? NULL : LoadCursor(NULL, IDC_ARROW)); diff --git a/examples/directx9_example/imgui_impl_dx9.cpp b/examples/directx9_example/imgui_impl_dx9.cpp index bb309a86..6341a2a8 100644 --- a/examples/directx9_example/imgui_impl_dx9.cpp +++ b/examples/directx9_example/imgui_impl_dx9.cpp @@ -338,6 +338,14 @@ void ImGui_ImplDX9_NewFrame() // io.MouseDown : filled by WM_*BUTTON* events // io.MouseWheel : filled by WM_MOUSEWHEEL events + // Set OS mouse position if requested last frame by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) + if (io.WantMoveMouse) + { + POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; + ClientToScreen(g_hWnd, &pos); + SetCursorPos(pos.x, pos.y); + } + // Hide OS mouse cursor if ImGui is drawing it SetCursor(io.MouseDrawCursor ? NULL : LoadCursor(NULL, IDC_ARROW)); diff --git a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp index 0e3a754c..4b9ab6a1 100644 --- a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp +++ b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp @@ -371,9 +371,16 @@ void ImGui_ImplGlfwGL3_NewFrame() // (we already got mouse wheel, keyboard keys & characters from glfw callbacks polled in glfwPollEvents()) if (glfwGetWindowAttrib(g_Window, GLFW_FOCUSED)) { - double mouse_x, mouse_y; - glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); - io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); // Mouse position in screen coordinates (set to -1,-1 if no mouse / on another screen, etc.) + if (io.WantMoveMouse) + { + glfwSetCursorPos(g_Window, (double)io.MousePos.x, (double)io.MousePos.y); // Set mouse position if requested by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) + } + else + { + double mouse_x, mouse_y; + glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); + io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); // Get mouse position in screen coordinates (set to -1,-1 if no mouse / on another screen, etc.) + } } else { diff --git a/examples/opengl_example/imgui_impl_glfw.cpp b/examples/opengl_example/imgui_impl_glfw.cpp index 2d1f5c1a..d7463150 100644 --- a/examples/opengl_example/imgui_impl_glfw.cpp +++ b/examples/opengl_example/imgui_impl_glfw.cpp @@ -262,9 +262,16 @@ void ImGui_ImplGlfw_NewFrame() // (we already got mouse wheel, keyboard keys & characters from glfw callbacks polled in glfwPollEvents()) if (glfwGetWindowAttrib(g_Window, GLFW_FOCUSED)) { - double mouse_x, mouse_y; - glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); - io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); // Mouse position in screen coordinates (set to -1,-1 if no mouse / on another screen, etc.) + if (io.WantMoveMouse) + { + glfwSetCursorPos(g_Window, (double)io.MousePos.x, (double)io.MousePos.y); // Set mouse position if requested by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) + } + else + { + double mouse_x, mouse_y; + glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); + io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); // Get mouse position in screen coordinates (set to -1,-1 if no mouse / on another screen, etc.) + } } else { From 68b73b61b5547c496ee94e8b27b27d1a5b7072e6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 23:17:32 +0200 Subject: [PATCH 058/319] Nav: Comment (#323) --- imgui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index 2f80b5b4..b657d80a 100644 --- a/imgui.h +++ b/imgui.h @@ -774,7 +774,7 @@ struct ImGuiIO int KeyMap[ImGuiKey_COUNT]; // // Map of indices into the KeysDown[512] entries array float KeyRepeatDelay; // = 0.250f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). float KeyRepeatRate; // = 0.020f // When holding a key/button, rate at which it repeats, in seconds. - bool NavMovesMouse; // = false // Directional navigation move the mouse cursor (update MousePos and set + bool NavMovesMouse; // = false // Directional navigation can move the mouse cursor. Updates MousePos and set WantMoveMouse=true. If enabled you MUST honor those requests in your binding, otherwise ImGui will react as if mouse is jumping around. void* UserData; // = NULL // Store your own data for retrieval by callbacks. ImFontAtlas* Fonts; // // Load and assemble one or more fonts into a single tightly packed texture. Output to Fonts array. From a88d189f181a30078cb08fa3051ac9a8f8c5c94f Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 30 Jul 2016 23:52:03 +0200 Subject: [PATCH 059/319] Nav: MoveRequest doesn't affect io.NavUsable so that navigation failure doesn't trigger false positive & not needed (undo part of 3cc1419df) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 0d5b3688..543ad9ef 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2475,7 +2475,7 @@ static void NavUpdate() // Set output flags for user application g.IO.NavUsable = g.FocusedWindow && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNavInputs); - g.IO.NavActive = (g.IO.NavUsable && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavMoveRequest || g.NavInitDefaultRequest; + g.IO.NavActive = (g.IO.NavUsable && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitDefaultRequest; // Process NavCancel input (to close a popup, get back to parent, clear focus) if (IsKeyPressedMap(ImGuiKey_NavCancel)) From 769a1dd748fa9a6c87bae26adf168434eb17bf61 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 31 Jul 2016 10:41:41 +0200 Subject: [PATCH 060/319] Nav: No scrolling while using windowing functionalities (#323) --- imgui.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 543ad9ef..72308283 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2549,23 +2549,23 @@ static void NavUpdate() } // Scrolling - if (g.FocusedWindow && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNavInputs)) + if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) { // 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)); + const float scroll_speed = ImFloor(g.NavWindow->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. + if (!g.NavWindow->DC.NavLayerActiveFlags && g.NavWindow->DC.NavHasScroll && g.NavMoveRequest && (g.NavMoveDir == ImGuiNavDir_Up || g.NavMoveDir == ImGuiNavDir_Down)) + SetWindowScrollY(g.NavWindow, ImFloor(g.NavWindow->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)); + SetWindowScrollX(g.NavWindow, ImFloor(g.NavWindow->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)); + SetWindowScrollY(g.NavWindow, ImFloor(g.NavWindow->Scroll.y + scroll_dir.y * scroll_speed)); g.NavMoveFromClampedRefRect = true; } } From b3cba62b80038bd8c8fd722ba5e4e13c83bfb8bc Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 31 Jul 2016 12:55:23 +0200 Subject: [PATCH 061/319] Nav: Added experiment ImGuiWindowFlags_NavFlattened flag to cross over between parent and non-scrolling child windows (#323) --- imgui.cpp | 38 ++++++++++++++++++++++---------------- imgui.h | 1 + imgui_demo.cpp | 2 +- imgui_internal.h | 7 ++++--- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 72308283..dcf3ea5f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1755,9 +1755,10 @@ ImGuiWindow::ImGuiWindow(const char* name) DrawList = (ImDrawList*)ImGui::MemAlloc(sizeof(ImDrawList)); IM_PLACEMENT_NEW(DrawList) ImDrawList(); DrawList->_OwnerName = Name; + ParentWindow = NULL; RootWindow = NULL; RootNonPopupWindow = NULL; - ParentWindow = NULL; + RootNavWindow = NULL; FocusIdxAllCounter = FocusIdxTabCounter = -1; FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX; @@ -1917,7 +1918,7 @@ static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1) static bool NavScoreItem(ImRect cand) { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; + ImGuiWindow* window = g.NavWindow; if (g.NavLayer != window->DC.NavLayerCurrent) return false; @@ -2059,10 +2060,10 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar // We could early out with `if (is_clipped && !g.NavInitDefaultRequest) return false;` but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) // A more pragmatic solution for handling last lists is relying on the fact that they are likely evenly spread items (so that clipper can work) and we could nav at higher-level (apply index, etc.) // So eventually we would like to provide the user will the primitives to be able to implement that sort of customized/efficient navigation handling whenever necessary. - if (id != NULL && g.NavWindow == window && g.IO.NavUsable) + if (id != NULL && g.NavWindow == window->RootNavWindow && g.IO.NavUsable) { const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; - const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); + const ImRect nav_bb_rel(nav_bb.Min - g.NavWindow->Pos, nav_bb.Max - g.NavWindow->Pos); if (g.NavInitDefaultRequest && g.NavLayer == window->DC.NavLayerCurrent && window->DC.AllowNavDefaultFocus) { g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = false; // Clear flag immediately, first item gets default, also simplify the if() in ItemAdd() @@ -2070,8 +2071,8 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar g.NavInitDefaultResultRectRel = nav_bb_rel; } - const bool DEBUG_NAV = false; // [DEBUG] Enable to test scoring on all items. - if ((g.NavMoveRequest || DEBUG_NAV) && g.NavId != *id) + //const bool DEBUG_NAV = false; // [DEBUG] Enable to test scoring on all items. + if ((g.NavMoveRequest /*|| DEBUG_NAV*/) && g.NavId != *id) { //if (DEBUG_NAV && !g.NavMoveRequest) g.NavMoveDir = ImGuiNavDir_N; if (NavScoreItem(nav_bb)) //if (!DEBUG || g.NavMoveRequest) @@ -2389,7 +2390,7 @@ static void NavUpdate() } } - // Apply result from previous navigation directional move request + // Apply result from previous frame navigation directional move request ImGui::SetActiveID(0); SetNavIdMoveMouse(g.NavMoveResultId, g.NavMoveResultRectRel); g.NavMoveFromClampedRefRect = false; @@ -3864,7 +3865,7 @@ void ImGui::SetItemAllowOverlap() void ImGui::SetItemDefaultFocus() { ImGuiContext& g = *GImGui; - if (g.NavWindow == g.CurrentWindow && (g.NavInitDefaultRequest || g.NavInitDefaultResultId != 0)) + if (g.NavWindow == g.CurrentWindow->RootNavWindow && (g.NavInitDefaultRequest || g.NavInitDefaultResultId != 0)) { g.NavInitDefaultRequest = false; g.NavInitDefaultResultExplicit = true; @@ -4186,7 +4187,7 @@ bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, // Process navigation-in immediately so NavInit can run on first frame const ImGuiID id = parent_window->GetChildID(child_window); - if ((child_window->DC.NavLayerActiveFlags != 0 || child_window->DC.NavHasScroll) && GImGui->NavActivateId == id) + if (!(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveFlags != 0 || child_window->DC.NavHasScroll) && GImGui->NavActivateId == id) { FocusWindow(child_window); NavInitWindow(child_window, false); @@ -4227,16 +4228,15 @@ void ImGui::EndChild() ImGuiID id = parent_window->GetChildID(window); ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); ItemSize(sz); - ItemAdd(bb, (window->DC.NavLayerActiveFlags != 0 || window->DC.NavHasScroll) ? &id : NULL); - if (window->DC.NavLayerActiveFlags != 0 || window->DC.NavHasScroll) + if (!(window->Flags & ImGuiWindowFlags_NavFlattened) && (window->DC.NavLayerActiveFlags != 0 || window->DC.NavHasScroll)) { - //if (!window->DC.NavHasItems && window->DC.NavHasScroll && g.NavWindow == window) // As a special case, we render nav highlight of child when inside when only scrolling is possible - //{ - // bb.Expand(-1.0f); - // id = g.NavId; - //} + ItemAdd(bb, &id); RenderNavHighlight(id, bb); } + else + { + ItemAdd(bb, NULL); + } } } @@ -4428,6 +4428,9 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us if (flags & ImGuiWindowFlags_NoInputs) flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; + if (flags & ImGuiWindowFlags_NavFlattened) + IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow); + // Find or create bool window_is_new = false; ImGuiWindow* window = FindWindowByName(name); @@ -4524,6 +4527,9 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->ParentWindow = parent_window; window->RootWindow = g.CurrentWindowStack[root_idx]; window->RootNonPopupWindow = g.CurrentWindowStack[root_non_popup_idx]; // Used to display TitleBgActive color and for selecting which window to use for NavWindowing + window->RootNavWindow = window; + while (window->RootNavWindow->Flags & ImGuiWindowFlags_NavFlattened) + window->RootNavWindow = window->RootNavWindow->ParentWindow; // When reusing window again multiple times a frame, just append content (don't need to setup again) if (first_begin_of_the_frame) diff --git a/imgui.h b/imgui.h index b657d80a..6da6cecd 100644 --- a/imgui.h +++ b/imgui.h @@ -511,6 +511,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) ImGuiWindowFlags_NoNavFocus = 1 << 17, // No focusing of this window with gamepad/keyboard navigation ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window + ImGuiWindowFlags_NavFlattened = 1 << 19, // Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) // [Internal] ImGuiWindowFlags_ChildWindow = 1 << 20, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_ChildWindowAutoFitX = 1 << 21, // Don't use! For internal use by BeginChild() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 7ec06dac..07b7dea9 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2443,7 +2443,7 @@ static void ShowExampleAppLayout(bool* p_open) ImGui::Separator(); ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "); ImGui::EndChild(); - ImGui::BeginChild("buttons"); + ImGui::BeginChild("buttons", ImVec2(0,0), false, ImGuiWindowFlags_NavFlattened); if (ImGui::Button("Revert")) {} ImGui::SameLine(); if (ImGui::Button("Save")) {} diff --git a/imgui_internal.h b/imgui_internal.h index 622d4b15..4b192fa1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -718,9 +718,10 @@ struct IMGUI_API ImGuiWindow ImGuiStorage StateStorage; float FontWindowScale; // Scale multiplier per-window ImDrawList* DrawList; - ImGuiWindow* RootWindow; // If we are a child window, this is pointing to the first non-child parent window. Else point to ourself. - ImGuiWindow* RootNonPopupWindow; // Used to display TitleBgActive color and for selecting which window to use for NavWindowing ImGuiWindow* ParentWindow; // Immediate parent in the window stack *regardless* of whether this window is a child window or not) + ImGuiWindow* RootWindow; // Generally point to ourself. If we are a child window, this is pointing to the first non-child parent window. + ImGuiWindow* RootNonPopupWindow; // Generally point to ourself. Used to display TitleBgActive color and for selecting which window to use for NavWindowing + ImGuiWindow* RootNavWindow; // Generally point to ourself. If we are a child window with the ImGuiWindowFlags_NavFlattenedChild flag, point to parent. Used to display TitleBgActive color and for selecting which window to use for NavWindowing. // Navigation / Focus // FIXME-NAVIGATION: Merge all this with the new Nav system, at least the request variables should be moved to ImGuiContext @@ -746,7 +747,7 @@ public: ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; } ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } - bool IsNavigableTo() const { return Active && this == this->RootNonPopupWindow && (!(Flags & ImGuiWindowFlags_NoNavFocus) || this == GImGui->FocusedWindow); } + bool IsNavigableTo() const { return Active && this == RootNonPopupWindow && (!(Flags & ImGuiWindowFlags_NoNavFocus) || this == GImGui->FocusedWindow); } }; //----------------------------------------------------------------------------- From 2545d75c3b978638329ac65cc1851f3f5998bb5b Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 31 Jul 2016 13:05:13 +0200 Subject: [PATCH 062/319] Tidying up, removed two unnecessary window flags from being exposed in imgui.h --- imgui.cpp | 15 +++++---------- imgui.h | 4 +--- imgui_internal.h | 1 + 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index dcf3ea5f..4fbc1b9e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1743,6 +1743,7 @@ ImGuiWindow::ImGuiWindow(const char* name) NavLastId = 0; AutoFitFramesX = AutoFitFramesY = -1; AutoFitOnlyGrows = false; + AutoFitChildAxises = 0x00; AutoPosLastDirection = -1; HiddenFrames = 0; SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiSetCond_Always | ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing; @@ -4161,18 +4162,11 @@ bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, const ImVec2 content_avail = GetContentRegionAvail(); ImVec2 size = ImFloor(size_arg); + const int auto_fit_axises = ((size.x == 0.0f) ? 0x01 : 0x00) | ((size.y == 0.0f) ? 0x02 : 0x00); if (size.x <= 0.0f) - { - if (size.x == 0.0f) - flags |= ImGuiWindowFlags_ChildWindowAutoFitX; size.x = ImMax(content_avail.x, 4.0f) - fabsf(size.x); // Arbitrary minimum zero-ish child size of 4.0f (0.0f causing too much issues) - } if (size.y <= 0.0f) - { - if (size.y == 0.0f) - flags |= ImGuiWindowFlags_ChildWindowAutoFitY; size.y = ImMax(content_avail.y, 4.0f) - fabsf(size.y); - } if (border) flags |= ImGuiWindowFlags_ShowBorders; flags |= extra_flags; @@ -4182,6 +4176,7 @@ bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, bool ret = ImGui::Begin(title, NULL, size, -1.0f, flags); ImGuiWindow* child_window = GetCurrentWindow(); + child_window->AutoFitChildAxises = auto_fit_axises; if (!(parent_window->Flags & ImGuiWindowFlags_ShowBorders)) child_window->Flags &= ~ImGuiWindowFlags_ShowBorders; @@ -4218,9 +4213,9 @@ void ImGui::EndChild() { // When using auto-filling child window, we don't provide full width/height to ItemSize so that it doesn't feed back into automatic size-fitting. ImVec2 sz = GetWindowSize(); - if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitX) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f + if (window->AutoFitChildAxises & 0x01) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f sz.x = ImMax(4.0f, sz.x); - if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitY) + if (window->AutoFitChildAxises & 0x02) sz.y = ImMax(4.0f, sz.y); ImGui::End(); diff --git a/imgui.h b/imgui.h index 6da6cecd..d3719ad7 100644 --- a/imgui.h +++ b/imgui.h @@ -513,9 +513,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window ImGuiWindowFlags_NavFlattened = 1 << 19, // Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) // [Internal] - ImGuiWindowFlags_ChildWindow = 1 << 20, // Don't use! For internal use by BeginChild() - ImGuiWindowFlags_ChildWindowAutoFitX = 1 << 21, // Don't use! For internal use by BeginChild() - ImGuiWindowFlags_ChildWindowAutoFitY = 1 << 22, // Don't use! For internal use by BeginChild() + ImGuiWindowFlags_ChildWindow = 1 << 22, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_ComboBox = 1 << 23, // Don't use! For internal use by ComboBox() ImGuiWindowFlags_Tooltip = 1 << 24, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Popup = 1 << 25, // Don't use! For internal use by BeginPopup() diff --git a/imgui_internal.h b/imgui_internal.h index 4b192fa1..b8d211ca 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -700,6 +700,7 @@ struct IMGUI_API ImGuiWindow ImGuiID NavLastId; // Last known NavId for this window, for nav layer 0 only. int AutoFitFramesX, AutoFitFramesY; bool AutoFitOnlyGrows; + int AutoFitChildAxises; int AutoPosLastDirection; int HiddenFrames; int SetWindowPosAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowPos() call will succeed with this particular flag. From 08a28c16ae71d6e5f40c5143929f19343c9ac22f Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 31 Jul 2016 13:38:57 +0200 Subject: [PATCH 063/319] Nav: Merge FocusedWindow and NavWindow that were basically duplicate at this point (#323) --- imgui.cpp | 65 ++++++++++++++++++++++-------------------------- imgui_internal.h | 10 +++----- 2 files changed, 34 insertions(+), 41 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4fbc1b9e..dd3ec4a9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2449,7 +2449,7 @@ static void NavUpdate() if (!IsKeyDownMap(ImGuiKey_NavMenu)) { // Apply actual focus only when releasing the NavMenu button (until then the window was merely rendered front-most) - if (g.NavWindowingTarget && (!g.FocusedWindow || g.NavWindowingTarget != g.FocusedWindow->RootNonPopupWindow)) + if (g.NavWindowingTarget && (!g.NavWindow || g.NavWindowingTarget != g.NavWindow->RootNonPopupWindow)) { ImGui::FocusWindow(g.NavWindowingTarget); g.NavDisableHighlight = false; @@ -2476,7 +2476,7 @@ static void NavUpdate() } // Set output flags for user application - g.IO.NavUsable = g.FocusedWindow && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNavInputs); + g.IO.NavUsable = g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); g.IO.NavActive = (g.IO.NavUsable && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitDefaultRequest; // Process NavCancel input (to close a popup, get back to parent, clear focus) @@ -2502,14 +2502,14 @@ static void NavUpdate() else { // Clear NavId for popups but keep it for regular child window so we can leave one and come back where we were - if (g.FocusedWindow && ((g.FocusedWindow->Flags & ImGuiWindowFlags_Popup) || !(g.FocusedWindow->Flags & ImGuiWindowFlags_ChildWindow))) - g.FocusedWindow->NavLastId = 0; + if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) + g.NavWindow->NavLastId = 0; - if (g.FocusedWindow && (g.FocusedWindow->Flags & ImGuiWindowFlags_ChildWindow) && g.FocusedWindow->ParentWindow) + if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && g.NavWindow->ParentWindow) { // Exit child window - ImGuiWindow* child_window = g.FocusedWindow; - ImGuiWindow* parent_window = g.FocusedWindow->ParentWindow; + ImGuiWindow* child_window = g.NavWindow; + ImGuiWindow* parent_window = g.NavWindow->ParentWindow; ImGui::FocusWindow(parent_window); g.NavId = parent_window->GetChildID(child_window); if (g.NavLayer == 0) @@ -2537,7 +2537,7 @@ static void NavUpdate() // Initiate directional inputs request const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; g.NavMoveDir = ImGuiNavDir_None; - if (g.FocusedWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNavInputs)) + if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { if ((allowed_dir_flags & (1<Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) @@ -2808,11 +2805,11 @@ void ImGui::NewFrame() // Pressing TAB activate widget focus //// NB: Don't discard FocusedWindow if it isn't active, so that a window that go on/off programatically won't lose its keyboard focus. // [2016/07/17] That comment was made invalid by 19d02becef94e8e0f1d432a8bd55cd783876583c - if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Active && !(g.FocusedWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false)) + if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false)) if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) - g.FocusedWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); + g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); else - g.FocusedWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0; + g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0; g.NavIdTabCounter = INT_MAX; // Mark all windows as not visible @@ -2825,7 +2822,7 @@ void ImGui::NewFrame() } // Closing the focused window restore focus to the first active root window in descending z-order - if (g.FocusedWindow && !g.FocusedWindow->WasActive) + if (g.NavWindow && !g.NavWindow->WasActive) for (int i = g.Windows.Size-1; i >= 0; i--) if (g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow)) { @@ -2867,7 +2864,7 @@ void ImGui::Shutdown() g.Windows.clear(); g.WindowsSortBuffer.clear(); g.CurrentWindowStack.clear(); - g.FocusedWindow = NULL; + g.NavWindow = NULL; g.HoveredWindow = NULL; g.HoveredRootWindow = NULL; for (int i = 0; i < g.Settings.Size; i++) @@ -3156,7 +3153,7 @@ void ImGui::EndFrame() // Click to focus window and start moving (after we're done with all our widgets) if (g.ActiveId == 0 && g.HoveredId == 0 && g.IO.MouseClicked[0]) { - if (!(g.FocusedWindow && !g.FocusedWindow->WasActive && g.FocusedWindow->Active)) // Unless we just made a popup appear + if (!(g.NavWindow && !g.NavWindow->WasActive && g.NavWindow->Active)) // Unless we just made a popup appear { if (g.HoveredRootWindow != NULL) { @@ -3173,7 +3170,7 @@ void ImGui::EndFrame() SetActiveIDNoNav(g.MovedWindowMoveId, g.HoveredRootWindow); } } - else if (g.FocusedWindow != NULL && GetFrontMostModalRootWindow() == NULL) + else if (g.NavWindow != NULL && GetFrontMostModalRootWindow() == NULL) { // Clicking on void disable focus FocusWindow(NULL); @@ -3626,7 +3623,7 @@ bool ImGui::IsAnyWindowHovered() bool ImGui::IsAnyWindowFocused() { - return GImGui->FocusedWindow != NULL; + return GImGui->NavWindow != NULL; } bool ImGui::IsAnyWindowHoveredAtPos(const ImVec2& pos) @@ -3981,7 +3978,7 @@ static void CloseInactivePopups() // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. // Don't close our own child popup windows int n = 0; - if (g.FocusedWindow) + if (g.NavWindow) { for (n = 0; n < g.OpenPopupStack.Size; n++) { @@ -3994,7 +3991,7 @@ static void CloseInactivePopups() bool has_focus = false; for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++) - has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == g.FocusedWindow->RootWindow); + has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == g.NavWindow->RootWindow); if (!has_focus) break; } @@ -4753,7 +4750,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us if (window->Collapsed) { // Title bar only - const bool is_focused = g.FocusedWindow && window->RootNonPopupWindow == g.FocusedWindow->RootNonPopupWindow && !g.NavDisableHighlight; + const bool is_focused = g.NavWindow && window->RootNonPopupWindow == g.NavWindow->RootNonPopupWindow && !g.NavDisableHighlight; RenderFrame(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32(is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed), true, window_rounding); } else @@ -4832,7 +4829,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, ColorConvertFloat4ToU32(bg_color), window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 15 : 4|8); // Title bar - const bool is_focused = g.FocusedWindow && window->RootNonPopupWindow == g.FocusedWindow->RootNonPopupWindow; + const bool is_focused = g.NavWindow && window->RootNonPopupWindow == g.NavWindow->RootNonPopupWindow; if (!(flags & ImGuiWindowFlags_NoTitleBar)) window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32(is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, 1|2); @@ -5164,7 +5161,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - if (g.FocusedWindow != window) + if (g.NavWindow != window) { g.NavId = window ? window->NavLastId : 0; // Restore NavId g.NavIdIsAlive = false; @@ -5176,7 +5173,6 @@ void ImGui::FocusWindow(ImGuiWindow* window) } // Passing NULL allow to disable keyboard focus - g.FocusedWindow = window; if (!window) return; @@ -5467,19 +5463,19 @@ bool ImGui::IsWindowHovered() bool ImGui::IsWindowFocused() { ImGuiContext& g = *GImGui; - return g.FocusedWindow == g.CurrentWindow; + return g.NavWindow == g.CurrentWindow; } bool ImGui::IsRootWindowFocused() { ImGuiContext& g = *GImGui; - return g.FocusedWindow == g.CurrentWindow->RootWindow; + return g.NavWindow == g.CurrentWindow->RootWindow; } bool ImGui::IsRootWindowOrAnyChildFocused() { ImGuiContext& g = *GImGui; - return g.FocusedWindow && g.FocusedWindow->RootWindow == g.CurrentWindow->RootWindow; + return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow; } bool ImGui::IsRootWindowOrAnyChildHovered() @@ -6144,8 +6140,8 @@ static inline bool IsWindowContentHoverable(ImGuiWindow* window) { // An active popup disable hovering on other windows (apart from its own children) ImGuiContext& g = *GImGui; - if (ImGuiWindow* focused_window = g.FocusedWindow) - if (ImGuiWindow* focused_root_window = focused_window->RootWindow) + if (g.NavWindow) + if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow) if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) != 0 && focused_root_window->WasActive && focused_root_window != window->RootWindow) return false; @@ -9623,13 +9619,13 @@ bool ImGui::BeginMenu(const char* label, bool enabled) const ImGuiID id = window->GetID(label); ImVec2 label_size = CalcTextSize(label, NULL, true); - ImGuiWindow* backed_focused_window = g.FocusedWindow; bool pressed; bool menu_is_open = IsPopupOpen(id); bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentMenuSet == window->GetID("##menus")); + ImGuiWindow* backed_nav_window = g.NavWindow; if (menuset_is_open) - g.FocusedWindow = window; + g.NavWindow = window; ImVec2 popup_pos, pos = window->DC.CursorPos; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) @@ -9656,7 +9652,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) bool hovered = enabled && IsHovered(window->DC.LastItemRect, id); if (menuset_is_open) - g.FocusedWindow = backed_focused_window; + g.NavWindow = backed_nav_window; bool want_open = false, want_close = false; if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) @@ -10596,12 +10592,11 @@ void ImGui::ShowMetricsWindow(bool* p_open) } if (ImGui::TreeNode("Basic state")) { - ImGui::Text("FocusedWindow: '%s'", g.FocusedWindow ? g.FocusedWindow->Name : "NULL"); ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); ImGui::Text("HoveredId: 0x%08X/0x%08X", g.HoveredId, g.HoveredIdPreviousFrame); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not ImGui::Text("ActiveId: 0x%08X/0x%08X, ActiveIdWindow: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); - ImGui::Text("NavId: 0x%08X, NavWindow: '%s'", g.NavId, g.NavWindow ? g.NavWindow->Name : "NULL"); + ImGui::Text("NavWindow: '%s', NavId: 0x%08X", g.NavWindow ? g.NavWindow->Name : "NULL", g.NavId); ImGui::Text("NavRefRectRel: (%.1f,%.1f)(%.1f,%.1f)", g.NavRefRectRel.Min.x, g.NavRefRectRel.Min.y, g.NavRefRectRel.Max.x, g.NavRefRectRel.Max.y); ImGui::Text("NavUsable: %d, NavActive: %d", g.IO.NavUsable, g.IO.NavActive); ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); diff --git a/imgui_internal.h b/imgui_internal.h index b8d211ca..60aa3215 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -378,7 +378,6 @@ struct ImGuiContext ImVector WindowsSortBuffer; ImVector CurrentWindowStack; ImGuiWindow* CurrentWindow; // Being drawn into - ImGuiWindow* FocusedWindow; // Will catch keyboard inputs ImGuiWindow* HoveredWindow; // Will catch mouse inputs ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) ImGuiID HoveredId; // Hovered widget @@ -404,11 +403,11 @@ struct ImGuiContext ImVector CurrentPopupStack; // Which level of BeginPopup() we are in (reset every frame) // Navigation data (for gamepad/keyboard) - ImGuiID NavId; // Nav/focused widget for navigation + ImGuiWindow* NavWindow; // Nav/focused window for navigation + ImGuiID NavId; // Nav/focused item for navigation ImGuiID NavActivateId, NavInputId; // ~~ IsKeyPressedMap(ImGuiKey_NavActive) ? NavId : 0, etc. (to make widget code terser) ImGuiID NavTabbedId; // ImRect NavRefRectRel, NavScoringRectScreen;// Reference rectangle, in window space. Modified rectangle for directional navigation scoring, in screen space. - ImGuiWindow* NavWindow; // ImGuiWindow* NavWindowingTarget; float NavWindowingDisplayAlpha; bool NavWindowingToggleLayer; @@ -497,7 +496,6 @@ struct ImGuiContext FrameCount = 0; FrameCountEnded = FrameCountRendered = -1; CurrentWindow = NULL; - FocusedWindow = NULL; HoveredWindow = NULL; HoveredRootWindow = NULL; HoveredId = 0; @@ -515,9 +513,9 @@ struct ImGuiContext MovedWindowMoveId = 0; SettingsDirtyTimer = 0.0f; + NavWindow = NULL; NavId = NavActivateId = NavInputId = NavTabbedId = 0; NavRefRectRel = NavScoringRectScreen = ImRect(); - NavWindow = NULL; NavWindowingTarget = NULL; NavWindowingDisplayAlpha = 0.0f; NavWindowingToggleLayer = false; @@ -748,7 +746,7 @@ public: ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; } ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } - bool IsNavigableTo() const { return Active && this == RootNonPopupWindow && (!(Flags & ImGuiWindowFlags_NoNavFocus) || this == GImGui->FocusedWindow); } + bool IsNavigableTo() const { return Active && this == RootNonPopupWindow && (!(Flags & ImGuiWindowFlags_NoNavFocus) || this == GImGui->NavWindow); } }; //----------------------------------------------------------------------------- From a154625a5657975b43d2559bc786f07cbab0faa5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 31 Jul 2016 13:40:14 +0200 Subject: [PATCH 064/319] Nav: Not exposing ImGuiWindowFlags_NavFlattened because it requires much more work (#323) --- imgui.cpp | 12 ++++++------ imgui.h | 2 +- imgui_demo.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index dd3ec4a9..851d93fa 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4179,7 +4179,7 @@ bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, // Process navigation-in immediately so NavInit can run on first frame const ImGuiID id = parent_window->GetChildID(child_window); - if (!(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveFlags != 0 || child_window->DC.NavHasScroll) && GImGui->NavActivateId == id) + if (/*!(flags & ImGuiWindowFlags_NavFlattened) &&*/ (child_window->DC.NavLayerActiveFlags != 0 || child_window->DC.NavHasScroll) && GImGui->NavActivateId == id) { FocusWindow(child_window); NavInitWindow(child_window, false); @@ -4220,7 +4220,7 @@ void ImGui::EndChild() ImGuiID id = parent_window->GetChildID(window); ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); ItemSize(sz); - if (!(window->Flags & ImGuiWindowFlags_NavFlattened) && (window->DC.NavLayerActiveFlags != 0 || window->DC.NavHasScroll)) + if (/*!(window->Flags & ImGuiWindowFlags_NavFlattened) &&*/ (window->DC.NavLayerActiveFlags != 0 || window->DC.NavHasScroll)) { ItemAdd(bb, &id); RenderNavHighlight(id, bb); @@ -4420,8 +4420,8 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us if (flags & ImGuiWindowFlags_NoInputs) flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; - if (flags & ImGuiWindowFlags_NavFlattened) - IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow); + //if (flags & ImGuiWindowFlags_NavFlattened) + // IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow); // Find or create bool window_is_new = false; @@ -4520,8 +4520,8 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->RootWindow = g.CurrentWindowStack[root_idx]; window->RootNonPopupWindow = g.CurrentWindowStack[root_non_popup_idx]; // Used to display TitleBgActive color and for selecting which window to use for NavWindowing window->RootNavWindow = window; - while (window->RootNavWindow->Flags & ImGuiWindowFlags_NavFlattened) - window->RootNavWindow = window->RootNavWindow->ParentWindow; + //while (window->RootNavWindow->Flags & ImGuiWindowFlags_NavFlattened) + // window->RootNavWindow = window->RootNavWindow->ParentWindow; // When reusing window again multiple times a frame, just append content (don't need to setup again) if (first_begin_of_the_frame) diff --git a/imgui.h b/imgui.h index d3719ad7..3745d5f4 100644 --- a/imgui.h +++ b/imgui.h @@ -511,7 +511,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) ImGuiWindowFlags_NoNavFocus = 1 << 17, // No focusing of this window with gamepad/keyboard navigation ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window - ImGuiWindowFlags_NavFlattened = 1 << 19, // Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) + //ImGuiWindowFlags_NavFlattened = 1 << 19, // Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) // [Internal] ImGuiWindowFlags_ChildWindow = 1 << 22, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_ComboBox = 1 << 23, // Don't use! For internal use by ComboBox() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 07b7dea9..7ec06dac 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2443,7 +2443,7 @@ static void ShowExampleAppLayout(bool* p_open) ImGui::Separator(); ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "); ImGui::EndChild(); - ImGui::BeginChild("buttons", ImVec2(0,0), false, ImGuiWindowFlags_NavFlattened); + ImGui::BeginChild("buttons"); if (ImGui::Button("Revert")) {} ImGui::SameLine(); if (ImGui::Button("Save")) {} From 1ecbf73c972ca48ec7ef4d36409287e87a5e3e26 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 31 Jul 2016 15:41:51 +0200 Subject: [PATCH 065/319] Nav: working on early bits of documentation (#323) --- imgui.cpp | 174 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 104 insertions(+), 70 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 851d93fa..a9a79fd2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -51,22 +51,21 @@ Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes: - doesn't look fancy, doesn't animate - limited layout features, intricate layouts are typically crafted in code - - occasionally uses statically sized buffers for string manipulations - won't crash, but some very long pieces of text may be clipped. functions like ImGui::TextUnformatted() don't have such restriction. END-USER GUIDE ============== - - double-click title bar to collapse window - - click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin() - - click and drag on lower right corner to resize window - - click and drag on any empty space to move window - - double-click/double-tap on lower right corner grip to auto-fit to content + - Double-click title bar to collapse window + - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin() + - Click and drag on lower right corner to resize window + - Click and drag on any empty space to move window + - Double-click/double-tap on lower right corner grip to auto-fit to content - TAB/SHIFT+TAB to cycle through keyboard editable fields - - use mouse wheel to scroll - - use CTRL+mouse wheel to zoom window contents (if IO.FontAllowScaling is true) + - Use mouse wheel to scroll + - Use CTRL+mouse wheel to zoom window contents (if io.FontAllowScaling is true) - CTRL+Click on a slider or drag box to input value as text - - text editor: + - Text editor: - Hold SHIFT or use mouse to select text. - CTRL+Left/Right to word jump - CTRL+Shift+Left/Right to select words @@ -75,75 +74,109 @@ - CTRL+Z,CTRL+Y to undo/redo - ESCAPE to revert text to its original value - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!) + - Controls are automatically adjusted for OSX to match standard OSX text editing operations. + - Gamepad/keyboard navigation are in beta-phase, see Programmer Guide below. PROGRAMMER GUIDE ================ - - read the FAQ below this section! - - your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention on your side, no state duplication, less sync, less bugs. - - call and read ImGui::ShowTestWindow() for demo code demonstrating most features. - - see examples/ folder for standalone sample applications. Prefer reading examples/opengl_example/ first as it is the simplest. - you may be able to grab and copy a ready made imgui_impl_*** file from the examples/. - - customization: PushStyleColor()/PushStyleVar() or the style editor to tweak the look of the interface (e.g. if you want a more compact UI or a different color scheme). + READ FIRST - - getting started: - - init: call ImGui::GetIO() to retrieve the ImGuiIO structure and fill the fields marked 'Settings'. - - init: call io.Fonts->GetTexDataAsRGBA32(...) and load the font texture pixels into graphics memory. - - every frame: - 1/ in your mainloop or right after you got your keyboard/mouse info, call ImGui::GetIO() and fill the fields marked 'Input' - 2/ call ImGui::NewFrame() as early as you can! - 3/ use any ImGui function you want between NewFrame() and Render() - 4/ call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your RenderDrawListFn handler that you set in the IO structure. - (if you don't need to render, you still need to call Render() and ignore the callback, or call EndFrame() instead. if you call neither some aspects of windows focusing/moving will appear broken.) - - all rendering information are stored into command-lists until ImGui::Render() is called. - - ImGui never touches or know about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide. - - effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases of your own application. - - refer to the examples applications in the examples/ folder for instruction on how to setup your code. - - a typical application skeleton may be: + - Read the FAQ below this section! + - Your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention on your side, no state duplication, less sync, less bugs. + - Call and read ImGui::ShowTestWindow() for demo code demonstrating most features. + - Customization: PushStyleColor()/PushStyleVar() or the style editor to tweak the look of the interface (e.g. if you want a more compact UI or a different color scheme). - // Application init - ImGuiIO& io = ImGui::GetIO(); - io.DisplaySize.x = 1920.0f; - io.DisplaySize.y = 1280.0f; - io.IniFilename = "imgui.ini"; - io.RenderDrawListsFn = my_render_function; // Setup a render function, or set to NULL and call GetDrawData() after Render() to access the render data. - // TODO: Fill others settings of the io structure + GETTING STARTED WITH INTEGRATING IN YOUR ENGINE - // Load texture atlas - // There is a default font so you don't need to care about choosing a font yet - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(pixels, &width, &height); - // TODO: At this points you've got a texture pointed to by 'pixels' and you need to upload that your your graphic system - // TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID' + - See examples/ folder for standalone sample applications. Prefer reading examples/opengl_example/ first as it is the simplest. + You may be able to grab and copy a ready made imgui_impl_*** file from the examples/. + - Init: call ImGui::GetIO() to retrieve the ImGuiIO structure and fill the fields marked 'Settings'. + - Init: call io.Fonts->GetTexDataAsRGBA32(...) and load the font texture pixels into graphics memory. + - Every frame: + 1/ in your mainloop or right after you got your keyboard/mouse info, call ImGui::GetIO() and fill the fields marked 'Input' + 2/ call ImGui::NewFrame() as early as you can! + 3/ use any ImGui function you want between NewFrame() and Render() + 4/ call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your RenderDrawListFn handler that you set in the IO structure. + (if you don't need to render, you still need to call Render() and ignore the callback, or call EndFrame() instead. if you call neither some aspects of windows focusing/moving will appear broken.) + - All rendering information are stored into command-lists until ImGui::Render() is called. + - ImGui never touches or know about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide. + - Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases of your own application. + - Refer to the examples applications in the examples/ folder for instruction on how to setup your code. + - A typical application skeleton may be: - // Application main loop - while (true) - { - // 1) get low-level inputs (e.g. on Win32, GetKeyboardState(), or poll your events, etc.) - // TODO: fill all fields of IO structure and call NewFrame - ImGuiIO& io = ImGui::GetIO(); - io.DeltaTime = 1.0f/60.0f; - io.MousePos = mouse_pos; - io.MouseDown[0] = mouse_button_0; - io.MouseDown[1] = mouse_button_1; - io.KeysDown[i] = ... + // Application init + ImGuiIO& io = ImGui::GetIO(); + io.DisplaySize.x = 1920.0f; + io.DisplaySize.y = 1280.0f; + io.IniFilename = "imgui.ini"; + io.RenderDrawListsFn = my_render_function; // Setup a render function, or set to NULL and call GetDrawData() after Render() to access the render data. + // TODO: Fill others settings of the io structure - // 2) call NewFrame(), after this point you can use ImGui::* functions anytime - ImGui::NewFrame(); + // Load texture atlas (there is a default font so you don't need to care about choosing a font yet) + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(pixels, &width, &height); + // TODO: At this points you've got a texture pointed to by 'pixels' and you need to upload that your your graphic system + // TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID' - // 3) most of your application code here - MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End(); - MyGameRender(); // may use any ImGui functions + // Application main loop + while (true) + { + // 1) get low-level inputs (e.g. on Win32, GetKeyboardState(), or poll your events, etc.) + // TODO: fill all fields of IO structure and call NewFrame + ImGuiIO& io = ImGui::GetIO(); + io.DeltaTime = 1.0f/60.0f; + io.MousePos = mouse_pos; + io.MouseDown[0] = mouse_button_0; + io.MouseDown[1] = mouse_button_1; + io.KeysDown[i] = ... - // 4) render & swap video buffers - ImGui::Render(); - SwapBuffers(); - } + // 2) call NewFrame(), after this point you can use ImGui::* functions anytime + ImGui::NewFrame(); - - You can read back 'io.WantCaptureMouse', 'io.WantCaptureKeybord' etc. flags from the IO structure to tell how ImGui intends to use your - inputs and to know if you should share them or hide them from the rest of your application. Read the FAQ below for more information. + // 3) most of your application code here + MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End(); + MyGameRender(); // may use any ImGui functions + + // 4) render & swap video buffers + ImGui::Render(); + SwapBuffers(); + } + + - You can read back 'io.WantCaptureMouse', 'io.WantCaptureKeybord' etc. flags from the IO structure to tell how ImGui intends to use your + inputs and to know if you should share them or hide them from the rest of your application. Read the FAQ below for more information. + + USING GAMEPAD/KEYBOARD NAVIGATION [BETA] + + - Gamepad/keyboard navigation support is available, it is currently in Beta and has issues. Your feedback and bug reports are welcome. + - See https://github.com/ocornut/imgui/issues/323 for discussion thread and ask questions. + - The current primary focus is to support game controllers. + - Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. + - Consider using Synergy host (on your computer) + uSynergy.c (in your console/tablet/phone app) to use PC mouse/keyboard. + - Being able to share and transition inputs between imgui navigation and your own game/application is tricky, and may requires further work on imgui. + For gamepad use, the easiest approach is to go all-or-nothing, with a buttons combo that toggle your inputs between imgui and your game/application. + For more advanced uses, you may want to use: + - io.NavUsable: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. + - io.NavActive: true when the navigation cursor is visible (and usually goes false when mouse is used). + - query focus information with IsWindowFocused(), IsAnyWindowFocused(), IsAnyItemFocused() functions. + The reality is more complex than what those flags can express. Please discuss your issues and usage scenario in the thread above. + As we head toward more keyboard-oriented development this aspect will need to be improved. + - It is recommended that you enable the 'io.NavMovesMouse' option. Enabling it instructs ImGui that it can request moving your move cursor to track navigated items and ease readability. + When enabled and using directional navigation (with d-pad or arrow keys), the NewFrame() functions may alter 'io.MousePos' and set 'io.WantMoveMouse' to notify you that it did so. + When that happens your back-end will need to move the OS mouse cursor on the _next_ frame. The examples binding in examples/ do that. + + // Application init + io.NavMovesMouse = true; + + // Application main loop + if (io.WantMoveMouse) + MyFuncToSetMousePosition(io.MousePos.x, io.MousePos.y); + ImGui::NewFrame(); + + In a setup when you may not have easy control over the mouse cursor (e.g. uSynergy doesn't expose changing remote mouse cursor), + you might want to set a boolean to request ignoring your other external mouse positions until they move again. API BREAKING CHANGES @@ -266,7 +299,7 @@ Q: How can I help? A: - If you are experienced enough with ImGui and with C/C++, look at the todo list and see how you want/can help! - - Become a Patron/donate. Convince your company to become a Patron or provide serious funding for development time. + - Become a Patron/donate! Convince your company to become a Patron or provide serious funding for development time! See http://www.patreon.com/imgui Q: How do I update to a newer version of ImGui? A: Overwrite the following files: @@ -480,9 +513,9 @@ - window/tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic. - window: increase minimum size of a window with menus or fix the menu rendering so that it doesn't look odd. - draw-list: maintaining bounding box per command would allow to merge draw command when clipping isn't relied on (typical non-scrolling window or non-overflowing column would merge with previous command). -!- scrolling: allow immediately effective change of scroll if we haven't appended items yet +!- scrolling: allow immediately effective change of scroll after Begin() if we haven't appended items yet - splitter/separator: formalize the splitter idiom into an official api (we want to handle n-way split) (#319) - - widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc. + - widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc. (#395) - widgets: clean up widgets internal toward exposing everything. - widgets: add disabled and read-only modes (#211) - main: considering adding an Init() function? some constructs are awkward in the implementation because of the lack of them. @@ -532,7 +565,6 @@ - popups: border options. richer api like BeginChild() perhaps? (#197) - tooltip: tooltip that doesn't fit in entire screen seems to lose their "last prefered button" and may teleport when moving mouse - menus: local shortcuts, global shortcuts (#456, #126) - - menus: icons - menus: menubars: some sort of priority / effect of main menu-bar on desktop size? - menus: calling BeginMenu() twice with a same name doesn't seem to append nicely - statusbar: add a per-window status bar helper similar to what menubar does. @@ -563,6 +595,7 @@ - textwrapped: figure out better way to use TextWrapped() in an always auto-resize context (tooltip, etc.) (#249) - settings: write more decent code to allow saving/loading new fields - settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file + - stb: add defines to disable stb implementations - style: add window shadows. - style/optimization: store rounded corners in texture to use 1 quad per corner (filled and wireframe) to lower the cost of rounding. - style: color-box not always square? @@ -570,7 +603,7 @@ - style: try to make PushStyleVar() more robust to incorrect parameters (to be more friendly to edit & continues situation). - style/opt: PopStyleVar could be optimized by having GetStyleVar returns the type, using a table mapping stylevar enum to data type. - style: global scale setting. - - style: WindowPadding needs to be EVEN needs the 0.5 multiplier probably have a subtle effect on clip rectangle + - style: WindowPadding needs to be EVEN as the 0.5 multiplier used on this value probably have a subtle effect on clip rectangle - text: simple markup language for color change? - font: dynamic font atlas to avoid baking huge ranges into bitmap and make scaling easier. - font: small opt: for monospace font (like the defalt one) we can trim IndexXAdvance as long as trailing value is == FallbackXAdvance @@ -583,6 +616,7 @@ - log: let user copy any window content to clipboard easily (CTRL+C on windows? while moving it? context menu?). code is commented because it fails with multiple Begin/End pairs. - filters: set a current filter that tree node can automatically query to hide themselves - filters: handle wildcards (with implicit leading/trailing *), regexps + - filters: fuzzy matches (may use code at blog.forrestthewoods.com/4cffeed33fdb) - shortcuts: add a shortcut api, e.g. parse "&Save" and/or "Save (CTRL+S)", pass in to widgets or provide simple ways to use (button=activate, input=focus) !- keyboard: tooltip & combo boxes are messing up / not honoring keyboard tabbing - keyboard: full keyboard navigation and focus. (#323) From e9c881e4de58154b42decc51e4d413dba673e671 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 31 Jul 2016 16:07:58 +0200 Subject: [PATCH 066/319] Nav: fixed using NavMenu/windowing select when no window is already focused + cleanup code (#323) --- imgui.cpp | 54 ++++++++++++++++++++++++++++++++---------------- imgui_internal.h | 1 - 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a9a79fd2..e0f5a242 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2358,6 +2358,27 @@ static void SetNavIdMoveMouse(ImGuiID id, const ImRect& rect_rel) g.NavDisableMouseHover = true; } +static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N) +{ + ImGuiContext& g = *GImGui; + for (int i = g.Windows.Size-1; i >= 0; i--) + if (g.Windows[i] == window) + return i; + return -1; +} + +static ImGuiWindow* FindWindowNavigable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) +{ + ImGuiContext& g = *GImGui; + for (int i = i_start; i >= 0 && i < g.Windows.Size && i != i_stop; i += dir) + { + ImGuiWindow* window = g.Windows[i]; + if (window->Active && window == window->RootNonPopupWindow && (!(window->Flags & ImGuiWindowFlags_NoNavFocus) || window == g.NavWindow)) + return window; + } + return NULL; +} + static void NavUpdate() { ImGuiContext& g = *GImGui; @@ -2432,11 +2453,17 @@ static void NavUpdate() } // Navigation windowing mode (change focus, move/resize window) - if (!g.NavWindowingTarget && g.NavWindow && IsKeyPressedMap(ImGuiKey_NavMenu, false)) + if (!g.NavWindowingTarget && IsKeyPressedMap(ImGuiKey_NavMenu, false)) { - g.NavWindowingTarget = g.NavWindow->RootNonPopupWindow; - g.NavWindowingDisplayAlpha = 0.0f; - g.NavWindowingToggleLayer = true; + ImGuiWindow* window = g.NavWindow; + if (!window) + window = FindWindowNavigable(g.Windows.Size-1, -1, -1); + if (window) + { + g.NavWindowingTarget = window->RootNonPopupWindow; + g.NavWindowingDisplayAlpha = 0.0f; + g.NavWindowingToggleLayer = true; + } } if (g.NavWindowingTarget) { @@ -2450,20 +2477,11 @@ static void NavUpdate() int focus_change_dir = IsKeyPressedMap(ImGuiKey_NavTweakFaster, true) ? -1 : IsKeyPressedMap(ImGuiKey_NavTweakSlower, true) ? +1 : 0; if (focus_change_dir != 0 && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)) { - // FIXME-NAVIGATION FIXME-OPT: Code is absolutely hideous. Perhaps we should maintain a intrusive linked-list of visible windows. - int i_current = -1; - for (int i = g.Windows.Size-1; i >= 0 && i_current == -1; i--) - if (g.Windows[i] == g.NavWindowingTarget) - i_current = i; - int i_target = -1; - for (int i = i_current+focus_change_dir; i >= 0 && i < g.Windows.Size && i_target == -1; i += focus_change_dir) - if (g.Windows[i]->IsNavigableTo()) - i_target = i; - for (int i = (focus_change_dir < 0) ? (g.Windows.Size-1) : 0; i >= 0 && i < g.Windows.Size && i_target == -1 && i_target != i_current; i += focus_change_dir) - if (g.Windows[i]->IsNavigableTo()) - i_target = i; - if (i_target != -1 && i_target != i_current) // i_target might be == i_current in rare situation where we only have 1 navigable window - g.NavWindowingTarget = g.Windows[i_target]; + const int i_current = FindWindowIndex(g.NavWindowingTarget); + ImGuiWindow* window_target = FindWindowNavigable(i_current + focus_change_dir, -1, focus_change_dir); + if (!window_target) + window_target = FindWindowNavigable((focus_change_dir < 0) ? (g.Windows.Size-1) : 0, i_current, focus_change_dir); + g.NavWindowingTarget = window_target; g.NavWindowingToggleLayer = false; g.NavWindowingDisplayAlpha = 1.0f; } diff --git a/imgui_internal.h b/imgui_internal.h index 60aa3215..35a206f1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -746,7 +746,6 @@ public: ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; } ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } - bool IsNavigableTo() const { return Active && this == RootNonPopupWindow && (!(Flags & ImGuiWindowFlags_NoNavFocus) || this == GImGui->NavWindow); } }; //----------------------------------------------------------------------------- From 8b190f11006d4588c1ad2bec5ff51a197c8ecd6f Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 31 Jul 2016 16:12:01 +0200 Subject: [PATCH 067/319] Nav: quick tap on NavMenu with no focused window doesn't focus one (holding still does) (#323) --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e0f5a242..65001eb5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2501,7 +2501,7 @@ static void NavUpdate() if (!IsKeyDownMap(ImGuiKey_NavMenu)) { // Apply actual focus only when releasing the NavMenu button (until then the window was merely rendered front-most) - if (g.NavWindowingTarget && (!g.NavWindow || g.NavWindowingTarget != g.NavWindow->RootNonPopupWindow)) + if (g.NavWindowingTarget && !g.NavWindowingToggleLayer && (!g.NavWindow || g.NavWindowingTarget != g.NavWindow->RootNonPopupWindow)) { ImGui::FocusWindow(g.NavWindowingTarget); g.NavDisableHighlight = false; @@ -2511,7 +2511,7 @@ static void NavUpdate() } // Single press toggles NavLayer - if (g.NavWindowingToggleLayer) + if (g.NavWindowingToggleLayer && g.NavWindow) { if ((g.NavWindow->DC.NavLayerActiveFlags & (1<<1)) == 0 && (g.NavWindow->RootWindow->DC.NavLayerActiveFlags & (1<<1)) != 0) ImGui::FocusWindow(g.NavWindow->RootWindow); From d0801057ba42c49c397a1a997ff9579d372c1d41 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 1 Aug 2016 20:55:54 +0200 Subject: [PATCH 068/319] Demo: Tweak handling of "animate" flag in graph demo so it doesn't keep running. --- imgui_demo.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 7ec06dac..2ad2b8f1 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -774,16 +774,16 @@ void ImGui::ShowTestWindow(bool* p_open) // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float and the sizeof() of your structure in the Stride parameter. static float values[90] = { 0 }; static int values_offset = 0; - if (animate) + static float refresh_time = 0.0f; + if (!animate || refresh_time == 0.0f) + refresh_time = ImGui::GetTime(); + while (refresh_time < ImGui::GetTime()) // Create dummy data at fixed 60 hz rate for the demo { - static float refresh_time = ImGui::GetTime(); // Create dummy data at fixed 60 hz rate for the demo - for (; ImGui::GetTime() > refresh_time + 1.0f/60.0f; refresh_time += 1.0f/60.0f) - { - static float phase = 0.0f; - values[values_offset] = cosf(phase); - values_offset = (values_offset+1) % IM_ARRAYSIZE(values); - phase += 0.10f*values_offset; - } + static float phase = 0.0f; + values[values_offset] = cosf(phase); + values_offset = (values_offset+1) % IM_ARRAYSIZE(values); + phase += 0.10f*values_offset; + refresh_time += 1.0f/60.0f; } ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, "avg 0.0", -1.0f, 1.0f, ImVec2(0,80)); ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0,80)); From 36fa2b95232911bca069f582bfdac2067547452c Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 1 Aug 2016 22:23:56 +0200 Subject: [PATCH 069/319] Nav: DragBehavior removed bogus test (#323) --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index dc1f1142..08c215e3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7627,9 +7627,9 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s if (g.ActiveIdSource == ImGuiInputSource_Nav) { adjust_delta = NavGetTweakDelta().x; - if (IsKeyDownMap(ImGuiKey_NavTweakFaster) && g.DragSpeedScaleFast >= 0.0f) + if (IsKeyDownMap(ImGuiKey_NavTweakFaster)) adjust_delta *= 10.0f; - if (IsKeyDownMap(ImGuiKey_NavTweakSlower) && g.DragSpeedScaleSlow >= 0.0f) + if (IsKeyDownMap(ImGuiKey_NavTweakSlower)) adjust_delta /= 10.0f; } adjust_delta *= v_speed; From 4e91b521ee6108d07ee5c3d3aa0eb427496614f9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 3 Aug 2016 20:55:54 +0200 Subject: [PATCH 070/319] Demo: Arrange some inputs panels. --- imgui_demo.cpp | 65 +++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2ad2b8f1..3050a2c4 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1499,7 +1499,7 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::BulletText("%s", lines[i]); } - if (ImGui::CollapsingHeader("Keyboard, Mouse & Focus")) + if (ImGui::CollapsingHeader("Inputs, Navigation & Focus")) { ImGuiIO& io = ImGui::GetIO(); ImGui::Checkbox("io.NavMovesMouse", &io.NavMovesMouse); @@ -1508,6 +1508,38 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); ImGui::SameLine(); ShowHelpMarker("Request ImGui to render a mouse cursor for you in software. Note that a mouse cursor rendered via regular GPU rendering will feel more laggy than hardware cursor, but will be more in sync with your other visuals."); + ImGui::Text("WantCaptureMouse: %d", io.WantCaptureMouse); + ImGui::Text("WantCaptureKeyboard: %d", io.WantCaptureKeyboard); + ImGui::Text("WantTextInput: %d", io.WantTextInput); + ImGui::Text("WantMoveMouse: %d", io.WantMoveMouse); + + if (ImGui::TreeNode("Keyboard, Mouse & Navigation State")) + { + ImGui::Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y); + ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } + ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text("Mouse dbl-clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); + + ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (io.KeysDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("%d (%.02f secs)", i, io.KeysDownDuration[i]); } + ImGui::Text("Keys pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyPressed(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } + ImGui::Text("Keys release:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyReleased(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } + ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); + + ImGui::Text("NavUsable: %d, NavActive: %d", io.NavUsable, io.NavActive); + + ImGui::Button("Hovering me sets the\nkeyboard capture flag"); + if (ImGui::IsItemHovered()) + ImGui::CaptureKeyboardFromApp(true); + ImGui::SameLine(); + ImGui::Button("Holding me clears the\nthe keyboard capture flag"); + if (ImGui::IsItemActive()) + ImGui::CaptureKeyboardFromApp(false); + + ImGui::TreePop(); + } + if (ImGui::TreeNode("Tabbing")) { ImGui::Text("Use TAB/SHIFT+TAB to cycle through keyboard editable fields."); @@ -1571,37 +1603,6 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::TreePop(); } - if (ImGui::TreeNode("Keyboard & Mouse State")) - { - ImGui::Text("MousePos: (%g, %g)", io.MousePos.x, io.MousePos.y); - ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } - ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse dbl-clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("MouseWheel: %.1f", io.MouseWheel); - - ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (io.KeysDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("%d (%.02f secs)", i, io.KeysDownDuration[i]); } - ImGui::Text("Keys pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyPressed(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } - ImGui::Text("Keys release:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyReleased(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } - ImGui::Text("KeyMods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); - - ImGui::Text("WantCaptureMouse: %d", io.WantCaptureMouse); - ImGui::Text("WantCaptureKeyboard: %d", io.WantCaptureKeyboard); - ImGui::Text("WantTextInput: %d", io.WantTextInput); - ImGui::Text("WantMoveMouse: %d", io.WantMoveMouse); - ImGui::Text("NavUsable: %d, NavActive: %d", io.NavUsable, io.NavActive); - - ImGui::Button("Hovering me sets the\nkeyboard capture flag"); - if (ImGui::IsItemHovered()) - ImGui::CaptureKeyboardFromApp(true); - ImGui::SameLine(); - ImGui::Button("Holding me clears the\nthe keyboard capture flag"); - if (ImGui::IsItemActive()) - ImGui::CaptureKeyboardFromApp(false); - - ImGui::TreePop(); - } - if (ImGui::TreeNode("Mouse cursors")) { ImGui::Text("Hover to see mouse cursors:"); From c0dcef4e1657036905cc2b6a808a0561332ea364 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 3 Aug 2016 21:02:27 +0200 Subject: [PATCH 071/319] Nav: Moving window with nav stick disables mouse hover (#323) --- imgui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.cpp b/imgui.cpp index 08c215e3..2efa6f89 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2493,6 +2493,7 @@ static void NavUpdate() { 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; + g.NavDisableMouseHover = true; MarkSettingsDirty(g.NavWindowingTarget); } } From 4ccc87c91d6079b7c59c5fb18b0d7bc6173660d3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 3 Aug 2016 21:53:07 +0200 Subject: [PATCH 072/319] Typo in commented default-value --- imgui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index bee4b465..d43cd8cd 100644 --- a/imgui.h +++ b/imgui.h @@ -772,7 +772,7 @@ struct ImGuiIO float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging int KeyMap[ImGuiKey_COUNT]; // // Map of indices into the KeysDown[512] entries array float KeyRepeatDelay; // = 0.250f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). - float KeyRepeatRate; // = 0.020f // When holding a key/button, rate at which it repeats, in seconds. + float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. bool NavMovesMouse; // = false // Directional navigation can move the mouse cursor. Updates MousePos and set WantMoveMouse=true. If enabled you MUST honor those requests in your binding, otherwise ImGui will react as if mouse is jumping around. void* UserData; // = NULL // Store your own data for retrieval by callbacks. From dcff032429c3afbca0d59e3ed68c37b60f1328a4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 3 Aug 2016 23:23:04 +0200 Subject: [PATCH 073/319] Nav: Moving all nav inputs to io.NavInputs[] float array, new enum labelled for gamepad. (#323) --- imgui.cpp | 188 ++++++++++++++++++++++++++--------------------- imgui.h | 50 +++++++------ imgui_demo.cpp | 3 + imgui_internal.h | 3 +- 4 files changed, 136 insertions(+), 108 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2efa6f89..52cab705 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -873,10 +873,9 @@ ImGuiIO::ImGuiIO() MousePos = ImVec2(-1,-1); MousePosPrev = ImVec2(-1,-1); MouseDragThreshold = 6.0f; - for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) - MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; - for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) - KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f; + for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; + for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f; + for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f; // Set OS X style defaults based on __APPLE__ compile time flag #ifdef __APPLE__ @@ -2378,6 +2377,60 @@ static ImGuiWindow* FindWindowNavigable(int i_start, int i_stop, int dir) // FIX return NULL; } +enum ImGuiNavReadMode +{ + ImGuiNavReadMode_Down, + ImGuiNavReadMode_Pressed, + ImGuiNavReadMode_Repeat, + ImGuiNavReadMode_RepeatSlow, + ImGuiNavReadMode_RepeatFast +}; + +// FIXME-NAVIGATION: Expose navigation repeat delay/rate +static float GetNavInputAmount(ImGuiNavInput n, ImGuiNavReadMode mode) +{ + ImGuiContext& g = *GImGui; + if (mode == ImGuiNavReadMode_Down) + return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) + const float t = g.IO.NavInputsDownDuration[n]; // Duration pressed + if (mode == ImGuiNavReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input (we don't need it for Pressed logic) + return (t == 0.0f) ? 1.0f : 0.0f; + if (mode == ImGuiNavReadMode_Repeat) + return (float)ImGui::CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f); + if (mode == ImGuiNavReadMode_RepeatSlow) + return (float)ImGui::CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f); + if (mode == ImGuiNavReadMode_RepeatFast) + return (float)ImGui::CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f); + return 0.0f; +} + +// Equivalent of IsKeyDown() for NavInputs[] +static bool IsNavInputDown(ImGuiNavInput n) +{ + return GImGui->IO.NavInputs[n] > 0.0f; +} + +// Equivalent of IsKeyPressed() for NavInputs[] +static bool IsNavInputPressed(ImGuiNavInput n, ImGuiNavReadMode mode)// = ImGuiNavReadMode_Re) +{ + return GetNavInputAmount(n, mode) > 0.0f; +} + +static ImVec2 GetNavInputAmount2d(int stick_no, ImGuiNavReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f) +{ + IM_ASSERT(ImGuiNavInput_PadScrollUp == ImGuiNavInput_PadUp + 4); + IM_ASSERT(stick_no >= 0 && stick_no < 2); + + ImVec2 delta; + delta.x = GetNavInputAmount(ImGuiNavInput_PadRight + stick_no*4, mode) - GetNavInputAmount(ImGuiNavInput_PadLeft + stick_no*4, mode); + delta.y = GetNavInputAmount(ImGuiNavInput_PadDown + stick_no*4, mode) - GetNavInputAmount(ImGuiNavInput_PadUp + stick_no*4, mode); + if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_PadTweakSlow)) + delta *= slow_factor; + if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_PadTweakFast)) + delta *= fast_factor; + return delta; +} + static void NavUpdate() { ImGuiContext& g = *GImGui; @@ -2452,7 +2505,7 @@ static void NavUpdate() } // Navigation windowing mode (change focus, move/resize window) - if (!g.NavWindowingTarget && IsKeyPressedMap(ImGuiKey_NavMenu, false)) + if (!g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiNavReadMode_Pressed)) { ImGuiWindow* window = g.NavWindow; if (!window) @@ -2467,13 +2520,13 @@ static void NavUpdate() if (g.NavWindowingTarget) { // Visuals only appears after a brief time holding the button, so that a fast tap (to toggle NavLayer) doesn't add visual noise - const float pressed_duration = g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_NavMenu]]; + const float pressed_duration = g.IO.NavInputsDownDuration[ImGuiNavInput_PadMenu]; g.NavWindowingDisplayAlpha = ImMax(g.NavWindowingDisplayAlpha, ImSaturate((pressed_duration - 0.20f) / 0.05f)); g.NavWindowingToggleLayer &= (g.NavWindowingDisplayAlpha < 1.0f); // Once button is held long enough we don't consider it a tag-to-toggle-layer press anymore. // Select window to focus // FIXME-NAVIGATION: Need to clarify input semantic, naming is misleading/incorrect here. - int focus_change_dir = IsKeyPressedMap(ImGuiKey_NavTweakFaster, true) ? -1 : IsKeyPressedMap(ImGuiKey_NavTweakSlower, true) ? +1 : 0; + const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_PadFocusPrev, ImGuiNavReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_PadFocusNext, ImGuiNavReadMode_RepeatSlow); if (focus_change_dir != 0 && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)) { const int i_current = FindWindowIndex(g.NavWindowingTarget); @@ -2488,7 +2541,7 @@ static void NavUpdate() // Move window if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) { - const ImVec2 move_delta = ImGui::NavGetMovingDir(1); + const ImVec2 move_delta = GetNavInputAmount2d(1, ImGuiNavReadMode_Down); 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)); @@ -2498,7 +2551,7 @@ static void NavUpdate() } } - if (!IsKeyDownMap(ImGuiKey_NavMenu)) + if (!IsNavInputDown(ImGuiNavInput_PadMenu)) { // Apply actual focus only when releasing the NavMenu button (until then the window was merely rendered front-most) if (g.NavWindowingTarget && !g.NavWindowingToggleLayer && (!g.NavWindow || g.NavWindowingTarget != g.NavWindow->RootNonPopupWindow)) @@ -2532,7 +2585,7 @@ static void NavUpdate() g.IO.NavActive = (g.IO.NavUsable && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitDefaultRequest; // Process NavCancel input (to close a popup, get back to parent, clear focus) - if (IsKeyPressedMap(ImGuiKey_NavCancel)) + if (IsNavInputPressed(ImGuiNavInput_PadCancel, ImGuiNavReadMode_Pressed)) { if (g.ActiveId != 0) { @@ -2577,8 +2630,8 @@ static void NavUpdate() } } - g.NavActivateId = (g.NavId && !g.NavDisableHighlight && !g.NavWindowingTarget && g.ActiveId == 0 && IsKeyPressedMap(ImGuiKey_NavActivate)) ? g.NavId : 0; - g.NavInputId = (g.NavId && !g.NavDisableHighlight && !g.NavWindowingTarget && g.ActiveId == 0 && IsKeyPressedMap(ImGuiKey_NavInput)) ? g.NavId : 0; + g.NavActivateId = (g.NavId && !g.NavDisableHighlight && !g.NavWindowingTarget && g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadActivate, ImGuiNavReadMode_Repeat)) ? g.NavId : 0; + g.NavInputId = (g.NavId && !g.NavDisableHighlight && !g.NavWindowingTarget && g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadInput, ImGuiNavReadMode_Repeat)) ? g.NavId : 0; if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { g.NavActivateId = g.NavInputId = 0; @@ -2591,10 +2644,10 @@ static void NavUpdate() g.NavMoveDir = ImGuiNavDir_None; if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { - if ((allowed_dir_flags & (1<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); + ImVec2 scroll_dir = GetNavInputAmount2d(1, ImGuiNavReadMode_Down, 1.0f/10.0f, 10.0f); if (scroll_dir.x != 0.0f && g.NavWindow->ScrollbarX) { SetWindowScrollX(g.NavWindow, ImFloor(g.NavWindow->Scroll.x + scroll_dir.x * scroll_speed)); @@ -2695,6 +2748,9 @@ void ImGui::NewFrame() memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f; + memcpy(g.IO.NavInputsPrev, g.IO.NavInputs, sizeof(g.IO.NavInputs)); + for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) + g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f; // Update directional navigation which may override MousePos if 'NavMovesMouse=true' NavUpdate(); @@ -3708,20 +3764,23 @@ bool ImGui::IsKeyDown(int key_index) return GImGui->IO.KeysDown[key_index]; } +int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate) +{ + if (t == 0.0f) + return 1; + if (t <= repeat_delay || repeat_rate <= 0.0f) + return 0; + const int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t_prev - repeat_delay) / repeat_rate); + return (count > 0) ? count : 0; +} + int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate) { ImGuiContext& g = *GImGui; if (key_index < 0) return false; IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); const float t = g.IO.KeysDownDuration[key_index]; - if (t == 0.0f) - return 1; - if (t > repeat_delay && repeat_rate > 0.0f) - { - int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t - repeat_delay - g.IO.DeltaTime) / repeat_rate); - return (count > 0) ? count : 0; - } - return 0; + return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate); } bool ImGui::IsKeyPressed(int key_index, bool repeat) @@ -4823,9 +4882,13 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us ImVec2 nav_resize_delta(0.0f, 0.0f); 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(0) * resize_speed; - held |= (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f); // For coloring + nav_resize_delta = GetNavInputAmount2d(0, ImGuiNavReadMode_Down); + if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) + { + nav_resize_delta *= ImFloor(600 * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); + g.NavDisableMouseHover = true; + held = true; // For coloring + } } ImVec2 size_target(FLT_MAX,FLT_MAX); @@ -6267,13 +6330,13 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool { // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse hovered = true; - if (!g.NavWindowingTarget && IsKeyDownMap(ImGuiKey_NavActivate)) + if (!g.NavWindowingTarget && IsNavInputDown(ImGuiNavInput_PadActivate)) { // Set active id so it can be queried by user via IsItemActive(), etc. but don't react to it ourselves - g.NavActivateId = g.NavId; - SetActiveID(g.NavId, window); + g.NavActivateId = id; + SetActiveID(id, window); g.ActiveIdAllowNavDirFlags = (1 << ImGuiNavDir_Left) | (1 << ImGuiNavDir_Right) | (1 << ImGuiNavDir_Up) | (1 << ImGuiNavDir_Down); - if (IsKeyPressedMap(ImGuiKey_NavActivate, (flags & ImGuiButtonFlags_Repeat) != 0)) + if (IsNavInputPressed(ImGuiNavInput_PadActivate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiNavReadMode_Repeat : ImGuiNavReadMode_Pressed)) pressed = true; } } @@ -6296,7 +6359,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool g.NavDisableHighlight = true; } if (g.ActiveId == id && g.ActiveIdSource == ImGuiInputSource_Nav) - if (!IsKeyDownMap(ImGuiKey_NavActivate)) + if (!IsNavInputDown(ImGuiNavInput_PadActivate)) SetActiveID(0); // 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. @@ -7136,47 +7199,6 @@ int ImGui::ParseFormatPrecision(const char* fmt, int default_precision) return precision; } -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 (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; -} - -// Adjustment delta for slider/drag/etc. -// FIXME-NAVIGATION: Accelerate over time? Expose more settings? Handle faster/slower modifiers here instead of widget level? -ImVec2 ImGui::NavGetTweakDelta() -{ - ImGuiContext& g = *GImGui; - float repeat_delay = g.IO.KeyRepeatDelay * 0.80f; - float repeat_rate = g.IO.KeyRepeatRate * 0.30f; - - ImVec2 delta(0.0f, 0.0f); - if (int count = GetKeyPressedAmount(g.IO.KeyMap[ImGuiKey_NavLeft], repeat_delay, repeat_rate)) delta.x = (float)-count; - if (int count = GetKeyPressedAmount(g.IO.KeyMap[ImGuiKey_NavRight], repeat_delay, repeat_rate)) delta.x = (float)+count; - if (int count = GetKeyPressedAmount(g.IO.KeyMap[ImGuiKey_NavUp], repeat_delay, repeat_rate)) delta.y = (float)-count; - if (int count = GetKeyPressedAmount(g.IO.KeyMap[ImGuiKey_NavDown], repeat_delay, repeat_rate)) delta.y = (float)+count; - return delta; -} - static float GetMinimumStepAtDecimalPrecision(int decimal_precision) { static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; @@ -7275,15 +7297,15 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v normalized_pos = 1.0f - normalized_pos; set_new_value = true; } - else if (g.ActiveIdSource == ImGuiInputSource_Nav && IsKeyDownMap(ImGuiKey_NavActivate)) + else if (g.ActiveIdSource == ImGuiInputSource_Nav && IsNavInputDown(ImGuiNavInput_PadActivate)) { - const ImVec2 delta2 = NavGetTweakDelta(); + const ImVec2 delta2 = GetNavInputAmount2d(0, ImGuiNavReadMode_RepeatFast, 0.0f, 0.0f); if (float delta = is_horizontal ? delta2.x : -delta2.y) { normalized_pos = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos); if (decimal_precision == 0 && !is_non_linear) { - if (fabsf(v_max - v_min) <= 100.0f || IsKeyDownMap(ImGuiKey_NavTweakSlower)) + if (fabsf(v_max - v_min) <= 100.0f || IsNavInputDown(ImGuiNavInput_PadTweakSlow)) delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (v_max - v_min); // Gamepad/keyboard tweak speeds in integer steps else delta /= 100.0f; @@ -7291,10 +7313,10 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v else { delta /= 100.0f; // Gamepad/keyboard tweak speeds in % of slider bounds - if (IsKeyDownMap(ImGuiKey_NavTweakSlower)) + if (IsNavInputDown(ImGuiNavInput_PadTweakSlow)) delta /= 10.0f; } - if (IsKeyDownMap(ImGuiKey_NavTweakFaster)) + if (IsNavInputDown(ImGuiNavInput_PadTweakFast)) delta *= 10.0f; normalized_pos = ImSaturate(normalized_pos + delta); // FIXME-NAVIGATION: todo: cancel adjustment if current value already past edge and we are moving in edge direction, to avoid clamping value to edge. set_new_value = true; @@ -7602,7 +7624,7 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s // Process clicking on the drag if (g.ActiveId == id) { - if (g.IO.MouseDown[0] || IsKeyDownMap(ImGuiKey_NavActivate)) + if (g.IO.MouseDown[0] || IsNavInputDown(ImGuiNavInput_PadActivate)) { if (g.ActiveIdIsJustActivated) { @@ -7627,11 +7649,7 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s } if (g.ActiveIdSource == ImGuiInputSource_Nav) { - adjust_delta = NavGetTweakDelta().x; - if (IsKeyDownMap(ImGuiKey_NavTweakFaster)) - adjust_delta *= 10.0f; - if (IsKeyDownMap(ImGuiKey_NavTweakSlower)) - adjust_delta /= 10.0f; + adjust_delta = GetNavInputAmount2d(0, ImGuiNavReadMode_RepeatFast, 1.0f/10.0f, 1.0f).x; } adjust_delta *= v_speed; g.DragLastMouseDelta.x = mouse_drag_delta.x; diff --git a/imgui.h b/imgui.h index d43cd8cd..c34d68c6 100644 --- a/imgui.h +++ b/imgui.h @@ -70,6 +70,7 @@ typedef void* ImTextureID; // user data to identify a texture (this is typedef int ImGuiCol; // a color identifier for styling // enum ImGuiCol_ typedef int ImGuiStyleVar; // a variable identifier for styling // enum ImGuiStyleVar_ typedef int ImGuiKey; // a key identifier (ImGui-side enum) // enum ImGuiKey_ +typedef int ImGuiNavInput; // an input identifier for gamepad nav // enum ImGuiNavInput_ typedef int ImGuiAlign; // alignment // enum ImGuiAlign_ typedef int ImGuiColorEditMode; // color edit mode for ColorEdit*() // enum ImGuiColorEditMode_ typedef int ImGuiMouseCursor; // a mouse cursor identifier // enum ImGuiMouseCursor_ @@ -437,7 +438,7 @@ namespace ImGui IMGUI_API bool IsKeyDown(int key_index); // key_index into the keys_down[] array, imgui doesn't know the semantic of each entry, uses your own indices! IMGUI_API bool IsKeyPressed(int key_index, bool repeat = true); // uses user's key indices as stored in the keys_down[] array. if repeat=true. uses io.KeyRepeatDelay / KeyRepeatRate IMGUI_API bool IsKeyReleased(int key_index); // " - IMGUI_API int GetKeyPressedAmount(int key_index, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, typically 0 or 1 but may be >1 if RepeatRate is small enough that DeltaTime > RepeatRate + IMGUI_API int GetKeyPressedAmount(int key_index, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate IMGUI_API bool IsMouseDown(int button); // is mouse button held IMGUI_API bool IsMouseClicked(int button, bool repeat = false); // did mouse button clicked (went from !Down to Down) IMGUI_API bool IsMouseDoubleClicked(int button); // did mouse button double-clicked. a double-click returns false in IsMouseClicked(). uses io.MouseDoubleClickTime. @@ -576,10 +577,10 @@ enum ImGuiSelectableFlags_ enum ImGuiKey_ { ImGuiKey_Tab, // for tabbing through fields - ImGuiKey_LeftArrow, // for text edit - ImGuiKey_RightArrow,// for text edit ImGuiKey_UpArrow, // for text edit ImGuiKey_DownArrow, // for text edit + ImGuiKey_LeftArrow, // for text edit + ImGuiKey_RightArrow,// for text edit ImGuiKey_PageUp, ImGuiKey_PageDown, ImGuiKey_Home, // for text edit @@ -594,27 +595,31 @@ enum ImGuiKey_ ImGuiKey_X, // for text edit CTRL+X: cut ImGuiKey_Y, // for text edit CTRL+Y: redo ImGuiKey_Z, // for text edit CTRL+Z: undo - - // Inputs for Gamepad/Keyboard navigation. Feed those buttons with the input of either or both peripherals involved. - ImGuiKey_NavActivate, // press button, tweak value // e.g. Space key, Circle button - ImGuiKey_NavCancel, // close menu/popup/child, unselect // e.g. Escape key, Cross button - ImGuiKey_NavInput, // text input // e.g. Enter key, Triangle button - ImGuiKey_NavMenu, // access menu, focus, move, resize // e.g. Square button - ImGuiKey_NavLeft, // e.g. Left arrow, D-Pad left - 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 }; +enum ImGuiNavInput_ +{ + ImGuiNavInput_PadActivate, // press button, tweak value // e.g. Circle button + ImGuiNavInput_PadCancel, // close menu/popup/child, lose selection // e.g. Cross button + ImGuiNavInput_PadInput, // text input // e.g. Triangle button + ImGuiNavInput_PadMenu, // access menu, focus, move, resize // e.g. Square button + ImGuiNavInput_PadUp, // move up, resize window (with PadMenu held) // e.g. D-pad up/down/left/right + ImGuiNavInput_PadDown, // move down + ImGuiNavInput_PadLeft, // move left + ImGuiNavInput_PadRight, // move right + ImGuiNavInput_PadScrollUp, // scroll up, move window (with PadMenu held) // e.g. right stick up/down/left/right + ImGuiNavInput_PadScrollDown, // " + ImGuiNavInput_PadScrollLeft, // + ImGuiNavInput_PadScrollRight, // + ImGuiNavInput_PadFocusPrev, // next window (with PadMenu held) // e.g. L-trigger + ImGuiNavInput_PadFocusNext, // prev window (with PadMenu held) // e.g. R-trigger + ImGuiNavInput_PadTweakSlow, // slower tweaks // e.g. L-trigger + ImGuiNavInput_PadTweakFast, // faster tweaks // e.g. R-trigger + + ImGuiNavInput_COUNT, +}; + // Enumeration for PushStyleColor() / PopStyleColor() enum ImGuiCol_ { @@ -824,6 +829,7 @@ struct ImGuiIO bool KeySuper; // Keyboard modifier pressed: Cmd/Super/Windows bool KeysDown[512]; // Keyboard keys that are pressed (in whatever storage order you naturally have access to keyboard data) ImWchar InputCharacters[16+1]; // List of characters input (translated by user from keypress+keyboard state). Fill using AddInputCharacter() helper. + float NavInputs[ImGuiNavInput_COUNT]; // Functions IMGUI_API void AddInputCharacter(ImWchar c); // Helper to add a new character into InputCharacters[] @@ -863,6 +869,8 @@ struct ImGuiIO float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the click point float KeysDownDuration[512]; // Duration the keyboard key has been down (0.0f == just pressed) float KeysDownDurationPrev[512]; // Previous duration the key has been down + float NavInputsDownDuration[ImGuiNavInput_COUNT]; + float NavInputsPrev[ImGuiNavInput_COUNT]; IMGUI_API ImGuiIO(); }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3050a2c4..6f0f7135 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1528,6 +1528,9 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); ImGui::Text("NavUsable: %d, NavActive: %d", io.NavUsable, io.NavActive); + ImGui::Text("NavInputs down:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputs[i] > 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f", i, io.NavInputs[i]); } + ImGui::Text("NavInputs pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] == 0.0f) { ImGui::SameLine(); ImGui::Text("[%d]", i); } + ImGui::Text("NavInputs duration:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f", i, io.NavInputsDownDuration[i]); } ImGui::Button("Hovering me sets the\nkeyboard capture flag"); if (ImGui::IsItemHovered()) diff --git a/imgui_internal.h b/imgui_internal.h index 35a206f1..deb4cf37 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -784,8 +784,7 @@ namespace ImGui IMGUI_API void OpenPopupEx(const char* str_id, bool reopen_existing); - IMGUI_API ImVec2 NavGetTweakDelta(); - IMGUI_API ImVec2 NavGetMovingDir(int stick_no, float slow_factor = 0.0f, float fast_factor = 0.0f); + IMGUI_API int CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate); 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); } From c263961f07fff4615c6390f449e0d4253bee8f89 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 4 Aug 2016 10:41:33 +0200 Subject: [PATCH 074/319] Nav: Menu: Allow PadDown to open a menu from a menu bar, PadRight from a menu (#323, #126) --- imgui.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 52cab705..07ab3908 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9754,14 +9754,25 @@ bool ImGui::BeginMenu(const char* label, bool enabled) want_close = menu_is_open; want_open = !menu_is_open; } + if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiNavDir_Right) // Nav-Right to open + { + want_open = true; + g.NavMoveRequest = false; + } } - else if (menu_is_open && pressed && menuset_is_open) // menu-bar: click open menu to close + else if (menu_is_open && pressed && menuset_is_open) // Menu bar: click an open menu again to close it { want_close = true; want_open = menu_is_open = false; } - else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // menu-bar: first click to open, then hover to open others + else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // Menu bar: click to open a first menu, then hover to open others want_open = true; + else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiNavDir_Down) // Menu bar: Nav-Down to open + { + g.NavMoveRequest = false; + want_open = true; + } + if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' want_close = true; if (want_close && IsPopupOpen(id)) From d85c1be6b6efbaeedad5736d30c37ac015b5e079 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 4 Aug 2016 12:53:46 +0200 Subject: [PATCH 075/319] Nav: Reordered Cancel handling code so you you can leave a child within a popup without closing the popup (#323) --- imgui.cpp | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 07ab3908..14a10af8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2591,13 +2591,27 @@ static void NavUpdate() { ImGui::SetActiveID(0); } + else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) + { + // Exit child window + ImGuiWindow* child_window = g.NavWindow; + ImGuiWindow* parent_window = g.NavWindow->ParentWindow; + ImGui::FocusWindow(parent_window); + g.NavId = parent_window->GetChildID(child_window); + if (g.NavLayer == 0) + parent_window->NavLastId = g.NavId; + g.NavIdIsAlive = false; + if (g.NavDisableMouseHover) + g.NavMousePosDirty = true; + } else if (g.OpenPopupStack.Size > 0) { - // Close open popup or move back to parent window + // Close open popup/menu ClosePopupToLevel(g.OpenPopupStack.Size - 1); } else if (g.NavLayer != 0) { + // Leave the "menu" layer g.NavLayer = 0; if (g.NavWindow->NavLastId) SetNavIdMoveMouse(g.NavWindow->NavLastId, ImRect()); @@ -2606,27 +2620,10 @@ static void NavUpdate() } else { - // Clear NavId for popups but keep it for regular child window so we can leave one and come back where we were + // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) g.NavWindow->NavLastId = 0; - - if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && g.NavWindow->ParentWindow) - { - // Exit child window - ImGuiWindow* child_window = g.NavWindow; - ImGuiWindow* parent_window = g.NavWindow->ParentWindow; - ImGui::FocusWindow(parent_window); - g.NavId = parent_window->GetChildID(child_window); - if (g.NavLayer == 0) - parent_window->NavLastId = g.NavId; - g.NavIdIsAlive = false; - if (g.NavDisableMouseHover) - g.NavMousePosDirty = true; - } - else - { - g.NavId = 0; - } + g.NavId = 0; } } From 5ef84525098079ca676a22bc59111663d8eb6455 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 7 Aug 2016 13:33:15 +0200 Subject: [PATCH 076/319] Nav: Comments (#323) --- imgui.cpp | 13 +++++++------ imgui.h | 14 +++++++++----- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0266135b..bbc27e86 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -150,13 +150,14 @@ USING GAMEPAD/KEYBOARD NAVIGATION [BETA] - - Gamepad/keyboard navigation support is available, it is currently in Beta and has issues. Your feedback and bug reports are welcome. + - Gamepad/keyboard navigation support is available, currently in Beta with some issues. Your feedback and bug reports are welcome. - See https://github.com/ocornut/imgui/issues/323 for discussion thread and ask questions. - The current primary focus is to support game controllers. - Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. - Consider using Synergy host (on your computer) + uSynergy.c (in your console/tablet/phone app) to use PC mouse/keyboard. - - Being able to share and transition inputs between imgui navigation and your own game/application is tricky, and may requires further work on imgui. - For gamepad use, the easiest approach is to go all-or-nothing, with a buttons combo that toggle your inputs between imgui and your game/application. + - Your inputs are passed to imgui by filling the io.NavInputs[] array. See 'enum ImGuiNavInput_' in imgui.h for a description of available inputs. + - For gamepad use, the easiest approach is to go all-or-nothing, with a buttons combo that toggle your inputs between imgui and your game/application. + Sharing inputs in a more advanced or granular way between imgui and your game/application may be tricky and requires further work on imgui. For more advanced uses, you may want to use: - io.NavUsable: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. - io.NavActive: true when the navigation cursor is visible (and usually goes false when mouse is used). @@ -165,7 +166,7 @@ As we head toward more keyboard-oriented development this aspect will need to be improved. - It is recommended that you enable the 'io.NavMovesMouse' option. Enabling it instructs ImGui that it can request moving your move cursor to track navigated items and ease readability. When enabled and using directional navigation (with d-pad or arrow keys), the NewFrame() functions may alter 'io.MousePos' and set 'io.WantMoveMouse' to notify you that it did so. - When that happens your back-end will need to move the OS mouse cursor on the _next_ frame. The examples binding in examples/ do that. + When that happens your back-end will need to move the OS mouse cursor on the next frame. The examples binding in examples/ do that. // Application init io.NavMovesMouse = true; @@ -175,8 +176,8 @@ MyFuncToSetMousePosition(io.MousePos.x, io.MousePos.y); ImGui::NewFrame(); - In a setup when you may not have easy control over the mouse cursor (e.g. uSynergy doesn't expose changing remote mouse cursor), - you might want to set a boolean to request ignoring your other external mouse positions until they move again. + In a setup when you may not have easy control over the mouse cursor (e.g. uSynergy.c doesn't expose moving remote mouse cursor), + you might want to set a boolean to ignore your other external mouse positions until they move again. API BREAKING CHANGES diff --git a/imgui.h b/imgui.h index e9a8d7bb..708a58f8 100644 --- a/imgui.h +++ b/imgui.h @@ -593,25 +593,29 @@ enum ImGuiKey_ ImGuiKey_COUNT }; +// [BETA] Gamepad/Keyboard directional navigation +// Fill ImGuiIO.NavInputs[] float array every frame to feed gamepad/keyboard navigation inputs. +// 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. +// ImGui uses a simple >0.0f for activation testing, and won't attempt to test for a dead-zone. +// Your code passing analog gamepad values is likely to want to transform your raw inputs, using a dead-zone and maybe a power curve. enum ImGuiNavInput_ { ImGuiNavInput_PadActivate, // press button, tweak value // e.g. Circle button ImGuiNavInput_PadCancel, // close menu/popup/child, lose selection // e.g. Cross button ImGuiNavInput_PadInput, // text input // e.g. Triangle button ImGuiNavInput_PadMenu, // access menu, focus, move, resize // e.g. Square button - ImGuiNavInput_PadUp, // move up, resize window (with PadMenu held) // e.g. D-pad up/down/left/right + ImGuiNavInput_PadUp, // move up, resize window (with PadMenu held) // e.g. D-pad up/down/left/right, analog ImGuiNavInput_PadDown, // move down ImGuiNavInput_PadLeft, // move left ImGuiNavInput_PadRight, // move right - ImGuiNavInput_PadScrollUp, // scroll up, move window (with PadMenu held) // e.g. right stick up/down/left/right + ImGuiNavInput_PadScrollUp, // scroll up, move window (with PadMenu held) // e.g. right stick up/down/left/right, analog ImGuiNavInput_PadScrollDown, // " ImGuiNavInput_PadScrollLeft, // ImGuiNavInput_PadScrollRight, // ImGuiNavInput_PadFocusPrev, // next window (with PadMenu held) // e.g. L-trigger ImGuiNavInput_PadFocusNext, // prev window (with PadMenu held) // e.g. R-trigger - ImGuiNavInput_PadTweakSlow, // slower tweaks // e.g. L-trigger - ImGuiNavInput_PadTweakFast, // faster tweaks // e.g. R-trigger - + ImGuiNavInput_PadTweakSlow, // slower tweaks // e.g. L-trigger, analog + ImGuiNavInput_PadTweakFast, // faster tweaks // e.g. R-trigger, analog ImGuiNavInput_COUNT, }; From 8a6d209f68411ebe12c21229494112fbd29ebecf Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 7 Aug 2016 13:34:25 +0200 Subject: [PATCH 077/319] Nav: Failed movement request with no current NavId fallback to an InitDefaultRequest so that we always land somewhere on first move (#323) --- imgui.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index bbc27e86..148e7723 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2664,6 +2664,14 @@ static void NavUpdate() if (g.NavMoveDir != ImGuiNavDir_None) g.NavMoveRequest = true; + // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match + if (g.NavMoveRequest && g.NavId == 0) + { + g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = true; + g.NavInitDefaultResultId = 0; + g.NavDisableHighlight = false; + } + // Scrolling if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) { From e55882f74b5608331f355f5240cd56a9e5937910 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 7 Aug 2016 15:18:04 +0200 Subject: [PATCH 078/319] Nav: Allow PadLeft to close a menu (#323) --- imgui.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 221b2147..c96fdf99 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9779,6 +9779,11 @@ bool ImGui::BeginMenu(const char* label, bool enabled) want_open = true; g.NavMoveRequest = false; } + if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveRequest && g.NavMoveDir == ImGuiNavDir_Left) // Nav-Left to close + { + want_close = true; + g.NavMoveRequest = false; + } } else if (menu_is_open && pressed && menuset_is_open) // Menu bar: click an open menu again to close it { From 6aa80197633f23bb0c7d72ac4731d6895766c613 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 7 Aug 2016 15:19:29 +0200 Subject: [PATCH 079/319] Nav: Fixed bug where pressing NavInput(Triangle) to turn slider/drag into text input would then change subsequent NavActivate as well (#323) --- imgui.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c96fdf99..d98505a2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2763,6 +2763,8 @@ void ImGui::NewFrame() g.ActiveIdPreviousFrame = g.ActiveId; g.ActiveIdIsAlive = false; g.ActiveIdIsJustActivated = false; + if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId) + g.ScalarAsInputTextId = 0; // Update keyboard input state memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); @@ -7167,6 +7169,7 @@ static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_b } // Create text input in place of a slider (when CTRL+Clicking on slider) +// FIXME: Logic is messy and confusing. bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision) { ImGuiContext& g = *GImGui; @@ -7181,18 +7184,12 @@ bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label char buf[32]; DataTypeFormatString(data_type, data_ptr, decimal_precision, buf, IM_ARRAYSIZE(buf)); bool text_value_changed = InputTextEx(label, buf, IM_ARRAYSIZE(buf), aabb.GetSize(), ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll); - if (g.ScalarAsInputTextId == 0) + if (g.ScalarAsInputTextId == 0) // First frame we started displaying the InputText widget { - // First frame IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID (else we'd need to store them both, which is also possible) g.ScalarAsInputTextId = g.ActiveId; SetHoveredID(id); } - else if (g.ActiveId != g.ScalarAsInputTextId) - { - // Release - g.ScalarAsInputTextId = 0; - } if (text_value_changed) return DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, NULL); return false; From a68132948b036393bc319db8bcd7192208f052b0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 7 Aug 2016 15:26:04 +0200 Subject: [PATCH 080/319] Nav: Added ImGuiCol_NavWindowingHighlight into style (#323) --- imgui.cpp | 11 +++++------ imgui.h | 1 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d98505a2..42f2a440 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -833,6 +833,7 @@ ImGuiStyle::ImGuiStyle() Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f); Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); + Colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.12f); } ImGuiIO::ImGuiIO() @@ -1961,8 +1962,7 @@ static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1) } // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057 -// FIXME-NAVIGATION: Pretty rough. -// FIXME-NAVIGATION: May want to handle the degenerate case that we have commented out. +// FIXME-NAVIGATION: Pretty rough. Also may want to handle the degenerate case that we have commented out. static bool NavScoreItem(ImRect cand) { ImGuiContext& g = *GImGui; @@ -2540,7 +2540,6 @@ static void NavUpdate() g.NavWindowingToggleLayer &= (g.NavWindowingDisplayAlpha < 1.0f); // Once button is held long enough we don't consider it a tag-to-toggle-layer press anymore. // Select window to focus - // FIXME-NAVIGATION: Need to clarify input semantic, naming is misleading/incorrect here. const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_PadFocusPrev, ImGuiNavReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_PadFocusNext, ImGuiNavReadMode_RepeatSlow); if (focus_change_dir != 0 && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)) { @@ -4868,13 +4867,12 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->DrawList->AddRectFilled(fullscreen_rect.Min, fullscreen_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); // Navigation windowing (via ImGuiKey_NavWindowing key) shows whole window selected - // FIXME-NAVIGATION: Styling if (g.NavWindowingTarget == window) { ImRect bb = window->Rect(); bb.Expand(g.FontSize); - window->DrawList->AddRectFilled(bb.Min, bb.Max, IM_COL32(255,255,255,(int)(30 * g.NavWindowingDisplayAlpha))/*ImGui::GetColorU32(ImGuiCol_HeaderHovered, 0.15f)*/, g.Style.WindowRounding); - window->DrawList->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_HeaderHovered, g.NavWindowingDisplayAlpha), g.Style.WindowRounding); + window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingDisplayAlpha), g.Style.WindowRounding); + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_HeaderHovered, g.NavWindowingDisplayAlpha), g.Style.WindowRounding); } // Draw window + handle manual resize @@ -5586,6 +5584,7 @@ const char* ImGui::GetStyleColName(ImGuiCol idx) case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered"; case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening"; + case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight"; } IM_ASSERT(0); return "Unknown"; diff --git a/imgui.h b/imgui.h index 708a58f8..d2cebfeb 100644 --- a/imgui.h +++ b/imgui.h @@ -665,6 +665,7 @@ enum ImGuiCol_ ImGuiCol_PlotHistogramHovered, ImGuiCol_TextSelectedBg, ImGuiCol_ModalWindowDarkening, // darken entire screen when a modal window is active + ImGuiCol_NavWindowingHighlight, // when holding NavMenu to focus/move/resize windows ImGuiCol_COUNT }; From 525ef5b357b09fe3417c4a191257be62a9710dfc Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 7 Aug 2016 15:31:00 +0200 Subject: [PATCH 081/319] Nav: Tweaked default highlight clipping (clipped by scrollbar) and removed subtle background (#323) --- imgui.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 42f2a440..7e3d690f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2080,8 +2080,7 @@ static void RenderNavHighlight(ImU32 id, const ImRect& bb) if (id != g.NavId || g.NavDisableHighlight) return; ImGuiWindow* window = ImGui::GetCurrentWindow(); - window->DrawList->PushClipRect(window->WindowRectClipped.Min - ImVec2(2,2), window->WindowRectClipped.Max + ImVec2(2,2)); - window->DrawList->AddRectFilled(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2), ImGui::GetColorU32(ImGuiCol_HeaderHovered, 0.15f), g.Style.FrameRounding); + window->DrawList->PushClipRect(window->InnerRect.Min - ImVec2(2,2), window->InnerRect.Max + ImVec2(2,2)); window->DrawList->AddRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2), ImGui::GetColorU32(ImGuiCol_HeaderHovered), g.Style.FrameRounding); //window->DrawList->AddRect(g.NavRefRectScreen.Min, g.NavRefRectScreen.Max, IM_COL32(255,0,0,255)); window->DrawList->PopClipRect(); From 785f51227a5222b83cb5fd21984fd32d0dc4eb3a Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 7 Aug 2016 19:07:24 +0200 Subject: [PATCH 082/319] Demo tweaks --- imgui_demo.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 6f0f7135..ff946e06 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -527,9 +527,10 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::TreePop(); } - static bool a=false; - if (ImGui::Button("Button")) { printf("Clicked\n"); a ^= 1; } - if (a) + static int clicked = 0; + if (ImGui::Button("Button")) + clicked++; + if (clicked & 1) { ImGui::SameLine(); ImGui::Text("Thanks for clicking me!"); @@ -1833,9 +1834,11 @@ static void ShowExampleMenuFile() ImGui::EndChild(); static float f = 0.5f; static int n = 0; + static bool b = true; ImGui::SliderFloat("Value", &f, 0.0f, 1.0f); ImGui::InputFloat("Input", &f, 0.1f); ImGui::Combo("Combo", &n, "Yes\0No\0Maybe\0\0"); + ImGui::Checkbox("Check", &b); ImGui::EndMenu(); } if (ImGui::BeginMenu("Colors")) From 8828889d5ec1d8fb9ddb72bab16d5e21ba8425c6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 7 Aug 2016 20:05:24 +0200 Subject: [PATCH 083/319] Refactored rare transient bool stacks into a set of flags, added unexposed ImGuiItemFlags_SelectableDontClosePopup (#323) --- imgui.cpp | 60 +++++++++++++++++++++++++++++------------------- imgui_internal.h | 24 ++++++++++++------- 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7e3d690f..1b5c5f02 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2111,7 +2111,7 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar { const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; const ImRect nav_bb_rel(nav_bb.Min - g.NavWindow->Pos, nav_bb.Max - g.NavWindow->Pos); - if (g.NavInitDefaultRequest && g.NavLayer == window->DC.NavLayerCurrent && window->DC.AllowNavDefaultFocus) + if (g.NavInitDefaultRequest && g.NavLayer == window->DC.NavLayerCurrent && (window->DC.ItemFlags & ImGuiItemFlags_AllowNavDefaultFocus)) { g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = false; // Clear flag immediately, first item gets default, also simplify the if() in ItemAdd() g.NavInitDefaultResultId = *id; @@ -2186,7 +2186,7 @@ bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop { ImGuiContext& g = *GImGui; - const bool allow_keyboard_focus = window->DC.AllowKeyboardFocus; + const bool allow_keyboard_focus = (window->DC.ItemFlags & ImGuiItemFlags_AllowKeyboardFocus) != 0; window->FocusIdxAllCounter++; if (allow_keyboard_focus) window->FocusIdxTabCounter++; @@ -5027,14 +5027,12 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; window->DC.ChildWindows.resize(0); window->DC.LayoutType = ImGuiLayoutType_Vertical; + window->DC.ItemFlags = ImGuiItemFlags_Default_; window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled - window->DC.AllowKeyboardFocus = window->DC.AllowNavDefaultFocus = true; - window->DC.ButtonRepeat = false; + window->DC.ItemFlagsStack.resize(0); window->DC.ItemWidthStack.resize(0); window->DC.TextWrapPosStack.resize(0); - window->DC.AllowKeyboardFocusStack.resize(0); - window->DC.ButtonRepeatStack.resize(0); window->DC.ColorEditMode = ImGuiColorEditMode_UserSelect; window->DC.ColumnsCurrent = 0; window->DC.ColumnsCount = 1; @@ -5045,6 +5043,12 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->DC.GroupStack.resize(0); window->MenuColumns.Update(3, style.ItemSpacing.x, !window_was_active); + if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags)) + { + window->DC.ItemFlags = parent_window->DC.ItemFlags; + window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags); + } + if (window->AutoFitFramesX > 0) window->AutoFitFramesX--; if (window->AutoFitFramesY > 0) @@ -5062,9 +5066,9 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us if (!(flags & ImGuiWindowFlags_NoTitleBar)) { // Close & collapse button are on layer 1 (same as menus) and don't default focus - const bool backup_allow_nav_default_focus = window->DC.AllowNavDefaultFocus; + const ImGuiItemFlags backup_item_options = window->DC.ItemFlags; if (window->Flags & ImGuiWindowFlags_MenuBar) - window->DC.AllowNavDefaultFocus = false; + window->DC.ItemFlags &= ~ImGuiItemFlags_AllowNavDefaultFocus; window->DC.NavLayerCurrent++; // Close button @@ -5090,7 +5094,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us } window->DC.NavLayerCurrent--; - window->DC.AllowNavDefaultFocus = backup_allow_nav_default_focus; + window->DC.ItemFlags = backup_item_options; // Title text (FIXME: refactor text alignment facilities along with RenderText helpers) ImVec2 text_min = window->Pos + style.FramePadding; @@ -5402,32 +5406,41 @@ void ImGui::PopFont() SetCurrentFont(g.FontStack.empty() ? g.IO.Fonts->Fonts[0] : g.FontStack.back()); } -void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus) +void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) { ImGuiWindow* window = GetCurrentWindow(); - window->DC.AllowKeyboardFocus = allow_keyboard_focus; - window->DC.AllowKeyboardFocusStack.push_back(allow_keyboard_focus); + if (enabled) + window->DC.ItemFlags |= option; + else + window->DC.ItemFlags &= ~option; + window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags); +} + +void ImGui::PopItemFlag() +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.ItemFlagsStack.pop_back(); + window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back(); +} + +void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus) +{ + PushItemFlag(ImGuiItemFlags_AllowKeyboardFocus, allow_keyboard_focus); } void ImGui::PopAllowKeyboardFocus() { - ImGuiWindow* window = GetCurrentWindow(); - window->DC.AllowKeyboardFocusStack.pop_back(); - window->DC.AllowKeyboardFocus = window->DC.AllowKeyboardFocusStack.empty() ? true : window->DC.AllowKeyboardFocusStack.back(); + PopItemFlag(); } void ImGui::PushButtonRepeat(bool repeat) { - ImGuiWindow* window = GetCurrentWindow(); - window->DC.ButtonRepeat = repeat; - window->DC.ButtonRepeatStack.push_back(repeat); + PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); } void ImGui::PopButtonRepeat() { - ImGuiWindow* window = GetCurrentWindow(); - window->DC.ButtonRepeatStack.pop_back(); - window->DC.ButtonRepeat = window->DC.ButtonRepeatStack.empty() ? false : window->DC.ButtonRepeatStack.back(); + PopItemFlag(); } void ImGui::PushTextWrapPos(float wrap_pos_x) @@ -5507,7 +5520,6 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) *pvar = val; } - void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) { ImGuiContext& g = *GImGui; @@ -6413,7 +6425,7 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags if (!ItemAdd(bb, &id)) return false; - if (window->DC.ButtonRepeat) flags |= ImGuiButtonFlags_Repeat; + if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) flags |= ImGuiButtonFlags_Repeat; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); @@ -9481,7 +9493,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); // Automatically close popups - if (pressed && !(flags & ImGuiSelectableFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) + if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !((window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup))) CloseCurrentPopup(); return pressed; } diff --git a/imgui_internal.h b/imgui_internal.h index 647adde3..f726d2a2 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -47,6 +47,7 @@ typedef int ImGuiLayoutType; // enum ImGuiLayoutType_ typedef int ImGuiButtonFlags; // enum ImGuiButtonFlags_ typedef int ImGuiTreeNodeFlags; // enum ImGuiTreeNodeFlags_ typedef int ImGuiSliderFlags; // enum ImGuiSliderFlags_ +typedef int ImGuiItemFlags; // enum ImGuiItemFlags_ //------------------------------------------------------------------------- // STB libraries @@ -574,6 +575,16 @@ struct ImGuiContext } }; +// Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin(). +enum ImGuiItemFlags_ +{ + ImGuiItemFlags_AllowKeyboardFocus = 1 << 0, // [true] + ImGuiItemFlags_AllowNavDefaultFocus = 1 << 1, // [true] + ImGuiItemFlags_ButtonRepeat = 1 << 2, // [false] // Button() can be held will a repeat behavior + ImGuiItemFlags_SelectableDontClosePopup = 1 << 3, // [false] // MenuItem/Selectable() automatically closes owner Popup window + ImGuiItemFlags_Default_ = ImGuiItemFlags_AllowKeyboardFocus|ImGuiItemFlags_AllowNavDefaultFocus +}; + // Transient per-window data, reset at the beginning of the frame // FIXME: That's theory, in practice the delimitation between ImGuiWindow and ImGuiDrawContext is quite tenuous and could be reconsidered. struct IMGUI_API ImGuiDrawContext @@ -602,15 +613,12 @@ struct IMGUI_API ImGuiDrawContext ImGuiLayoutType LayoutType; // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. + ImGuiItemFlags ItemFlags; // == ItemFlagsStack.back() [empty == ImGuiItemFlags_Default] float ItemWidth; // == ItemWidthStack.back(). 0.0: default, >0.0: width in pixels, <0.0: align xx pixels to the right of window float TextWrapPos; // == TextWrapPosStack.back() [empty == -1.0f] - bool AllowKeyboardFocus; // == AllowKeyboardFocusStack.back() [empty == true] - bool AllowNavDefaultFocus; // (not exposed via stack) - bool ButtonRepeat; // == ButtonRepeatStack.back() [empty == false] + ImVectorItemFlagsStack; ImVector ItemWidthStack; ImVector TextWrapPosStack; - ImVector AllowKeyboardFocusStack; - ImVector ButtonRepeatStack; ImVectorGroupStack; ImGuiColorEditMode ColorEditMode; int StackSizesBackup[6]; // Store size of various stacks for asserting @@ -647,8 +655,7 @@ struct IMGUI_API ImGuiDrawContext StateStorage = NULL; LayoutType = ImGuiLayoutType_Vertical; ItemWidth = 0.0f; - ButtonRepeat = false; - AllowKeyboardFocus = AllowNavDefaultFocus = true; + ItemFlags = ImGuiItemFlags_Default_; TextWrapPos = -1.0f; ColorEditMode = ImGuiColorEditMode_RGB; memset(StackSizesBackup, 0, sizeof(StackSizesBackup)); @@ -782,8 +789,9 @@ namespace ImGui IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); + IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); + IMGUI_API void PopItemFlag(); IMGUI_API void OpenPopupEx(const char* str_id, bool reopen_existing); - IMGUI_API int CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate); // NB: All position are in absolute pixels coordinates (not window coordinates) From 761a74c62b4d55fb42eae32bfa0b321c3f601ced Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 7 Aug 2016 20:21:18 +0200 Subject: [PATCH 084/319] Selectable/MenuItem: Not activated on Click but only on Release is more standard (#126, #245, #323) Apparently menu items started with OnClick (vs OnClickHoldRelease) when doing #126. Hope to not break anything. Also allows using xxx_DontClosePopup flags. --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 1b5c5f02..75c45e37 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9467,7 +9467,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl ImGuiButtonFlags button_flags = 0; if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick; - if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnClick|ImGuiButtonFlags_PressedOnRelease; + if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnRelease; if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled; if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; bool hovered, held; From 09cba02d3f0a1e84ed682dae27bfba48b3ecb6e4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 7 Aug 2016 20:31:51 +0200 Subject: [PATCH 085/319] Nav: DragBehavior: Fix for fast speed (#323 #180) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 75c45e37..69c07e20 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7676,7 +7676,7 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s } if (g.ActiveIdSource == ImGuiInputSource_Nav) { - adjust_delta = GetNavInputAmount2d(0, ImGuiNavReadMode_RepeatFast, 1.0f/10.0f, 1.0f).x; + adjust_delta = GetNavInputAmount2d(0, ImGuiNavReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; } adjust_delta *= v_speed; g.DragLastMouseDelta.x = mouse_drag_delta.x; From df1d1b5b25b8a7d36bfa7d69b0e83b2478acb487 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 7 Aug 2016 20:40:57 +0200 Subject: [PATCH 086/319] Nav: DragBehavior: Adjust minimum step to displayed precision when using navigation (#323, #180) --- imgui.cpp | 2 ++ imgui.h | 1 + 2 files changed, 3 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 69c07e20..ebe07cb7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7662,6 +7662,8 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s if (v_speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX) v_speed = (v_max - v_min) * g.DragSpeedDefaultRatio; + if (g.ActiveIdSource == ImGuiInputSource_Nav) + v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); float v_cur = g.DragCurrentValue; const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f); diff --git a/imgui.h b/imgui.h index d2cebfeb..5b0bc55d 100644 --- a/imgui.h +++ b/imgui.h @@ -282,6 +282,7 @@ namespace ImGui // Widgets: Drags (tip: ctrl+click on a drag box to input with keyboard. manually input values aren't clamped, can go off-bounds) // For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, remember than a 'float v[3]' function argument is the same as 'float* v'. You can pass address of your first element out of a contiguous set, e.g. &myvector.x + // Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision). IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); // If v_min >= v_max we have no bound IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); From 9501cd999151822d8d8232cccded8e7280967296 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 13 Aug 2016 14:22:34 +0200 Subject: [PATCH 087/319] InputText: Fixed calling callback on frame of losing active id, fix part of 848e62bfe0fe6083668afebdfaa5ab234a4817d9 (nav branch only) #323 #701 --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bf375ae9..e7a2a76a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8695,7 +8695,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } bool cancel_edit = false; - if (g.ActiveId == id && !g.ActiveIdIsJustActivated) + if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id) { // Handle key-presses const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); @@ -8806,7 +8806,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 value_changed = true; } } - else + if (!cancel_edit && !clear_active_id) { // Apply new value immediately - copy modified buffer back // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer From 6c19d7b13c3765b8df555dced1919f806f843759 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Aug 2016 11:41:56 +0200 Subject: [PATCH 088/319] Nav: Fixed clipping rect of navigation highlight, notably for collapsing arrow (#323) --- imgui.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e7a2a76a..252d795e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2080,10 +2080,14 @@ static void RenderNavHighlight(ImU32 id, const ImRect& bb) if (id != g.NavId || g.NavDisableHighlight) return; ImGuiWindow* window = ImGui::GetCurrentWindow(); - window->DrawList->PushClipRect(window->InnerRect.Min - ImVec2(2,2), window->InnerRect.Max + ImVec2(2,2)); + + ImRect clip_rect(window->InnerRect.Min - ImVec2(2,2), window->InnerRect.Max + ImVec2(2,2)); + if (!window->ClipRect.Contains(clip_rect)) + window->DrawList->PushClipRect(clip_rect.Min, clip_rect.Max); window->DrawList->AddRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2), ImGui::GetColorU32(ImGuiCol_HeaderHovered), g.Style.FrameRounding); //window->DrawList->AddRect(g.NavRefRectScreen.Min, g.NavRefRectScreen.Max, IM_COL32(255,0,0,255)); - window->DrawList->PopClipRect(); + if (!window->ClipRect.Contains(clip_rect)) + window->DrawList->PopClipRect(); } // Declare item bounding box for clipping and interaction. From cf16ba6572c1bdc00a1eb10d5e0984695d2f33a2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Aug 2016 11:52:02 +0200 Subject: [PATCH 089/319] Nav: Exposed RenderNavHighlight() in imgui_internal.h to increase discoverability and reordered arguments to be more consistent (#323) --- imgui.cpp | 24 ++++++++++++------------ imgui_internal.h | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 252d795e..a99a1713 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2074,7 +2074,7 @@ static bool NavScoreItem(ImRect cand) return new_best; } -static void RenderNavHighlight(ImU32 id, const ImRect& bb) +void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id) { ImGuiContext& g = *GImGui; if (id != g.NavId || g.NavDisableHighlight) @@ -4358,7 +4358,7 @@ void ImGui::EndChild() if (/*!(window->Flags & ImGuiWindowFlags_NavFlattened) &&*/ (window->DC.NavLayerActiveFlags != 0 || window->DC.NavHasScroll)) { ItemAdd(bb, &id); - RenderNavHighlight(id, bb); + RenderNavHighlight(bb, id); } else { @@ -5093,7 +5093,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us ItemAdd(bb, &id); // To allow navigation if (ButtonBehavior(bb, id, NULL, NULL)) window->CollapseToggleWanted = true; // Defer collapsing to next frame as we are too far in the Begin() function - RenderNavHighlight(id, bb); + RenderNavHighlight(bb, id); RenderCollapseTriangle(window->Pos + style.FramePadding, !window->Collapsed, 1.0f, true); } @@ -6435,7 +6435,7 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags // Render const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - RenderNavHighlight(id, bb); + RenderNavHighlight(bb, id); RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, ImGuiAlign_Center | ImGuiAlign_VCenter); @@ -6565,7 +6565,7 @@ bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const I // Render const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - RenderNavHighlight(id, bb); + RenderNavHighlight(bb, id); RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding)); if (bg_col.w > 0.0f) window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col)); @@ -7282,7 +7282,7 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v // Draw frame const ImU32 frame_col = GetColorU32((g.ActiveId == id && g.ActiveIdSource == ImGuiInputSource_Nav) ? ImGuiCol_FrameBgActive : ImGuiCol_FrameBg); - RenderNavHighlight(id, frame_bb); + RenderNavHighlight(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); const bool is_non_linear = (power < 1.0f-0.00001f) && (power > 1.0f-0.00001f); @@ -7647,7 +7647,7 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s // Draw frame const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - RenderNavHighlight(id, frame_bb); + RenderNavHighlight(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); bool value_changed = false; @@ -8143,7 +8143,7 @@ bool ImGui::Checkbox(const char* label, bool* v) if (pressed) *v = !(*v); - RenderNavHighlight(id, total_bb); + RenderNavHighlight(total_bb, id); RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); if (*v) { @@ -8210,7 +8210,7 @@ bool ImGui::RadioButton(const char* label, bool active) bool hovered, held; bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); - RenderNavHighlight(id, total_bb); + RenderNavHighlight(total_bb, id); window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16); if (active) { @@ -8906,7 +8906,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on. const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL; - RenderNavHighlight(id, frame_bb); + RenderNavHighlight(frame_bb, id); if (!is_multiline) RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); @@ -9330,7 +9330,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi bool popup_open = IsPopupOpen(id); const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f)); - RenderNavHighlight(id, frame_bb); + RenderNavHighlight(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_open || hovered || navigated ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING RenderCollapseTriangle(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y) + style.FramePadding, true); @@ -9861,7 +9861,7 @@ bool ImGui::ColorButton(const ImVec4& col, bool small_height, bool outline_borde bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held); - RenderNavHighlight(id, bb); + RenderNavHighlight(bb, id); RenderFrame(bb.Min, bb.Max, GetColorU32(col), outline_border, style.FrameRounding); if (g.HoveredId == id) diff --git a/imgui_internal.h b/imgui_internal.h index f726d2a2..0bbeba41 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -804,6 +804,7 @@ namespace ImGui IMGUI_API void RenderCollapseTriangle(ImVec2 pos, bool is_open, float scale = 1.0f, bool shadow = false); IMGUI_API void RenderBullet(ImVec2 pos); IMGUI_API void RenderCheckMark(ImVec2 pos, ImU32 col); + IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id); // Navigation highlight IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text. IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); From 3ab0d5cdfde91c6db6a4f51d3999ba36882bb2dd Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Aug 2016 13:07:39 +0200 Subject: [PATCH 090/319] Nav: NavHighlight gets its own color (default to Header color), made rectangle thicker (#323) --- imgui.cpp | 6 ++++-- imgui.h | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a99a1713..26ff777b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -833,6 +833,7 @@ ImGuiStyle::ImGuiStyle() Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f); Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); + Colors[ImGuiCol_NavHighlight] = Colors[ImGuiCol_HeaderHovered]; Colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.12f); } @@ -2081,10 +2082,10 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id) return; ImGuiWindow* window = ImGui::GetCurrentWindow(); - ImRect clip_rect(window->InnerRect.Min - ImVec2(2,2), window->InnerRect.Max + ImVec2(2,2)); + ImRect clip_rect(window->InnerRect.Min - ImVec2(4,4), window->InnerRect.Max + ImVec2(4,4)); if (!window->ClipRect.Contains(clip_rect)) window->DrawList->PushClipRect(clip_rect.Min, clip_rect.Max); - window->DrawList->AddRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2), ImGui::GetColorU32(ImGuiCol_HeaderHovered), g.Style.FrameRounding); + window->DrawList->AddRect(bb.Min - ImVec2(3,3), bb.Max + ImVec2(3,3), GetColorU32(ImGuiCol_NavHighlight), g.Style.FrameRounding, 0x0F, 2.0f); //window->DrawList->AddRect(g.NavRefRectScreen.Min, g.NavRefRectScreen.Max, IM_COL32(255,0,0,255)); if (!window->ClipRect.Contains(clip_rect)) window->DrawList->PopClipRect(); @@ -5599,6 +5600,7 @@ const char* ImGui::GetStyleColName(ImGuiCol idx) case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered"; case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening"; + case ImGuiCol_NavHighlight: return "NavHighlight"; case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight"; } IM_ASSERT(0); diff --git a/imgui.h b/imgui.h index 5b0bc55d..420df52d 100644 --- a/imgui.h +++ b/imgui.h @@ -666,7 +666,8 @@ enum ImGuiCol_ ImGuiCol_PlotHistogramHovered, ImGuiCol_TextSelectedBg, ImGuiCol_ModalWindowDarkening, // darken entire screen when a modal window is active - ImGuiCol_NavWindowingHighlight, // when holding NavMenu to focus/move/resize windows + ImGuiCol_NavHighlight, // gamepad/keyboard: current highlighted item + ImGuiCol_NavWindowingHighlight, // gamepad/keyboard: when holding NavMenu to focus/move/resize windows ImGuiCol_COUNT }; From ead79dcdac88ed4d06e2f42b83e0b27599c940b2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Aug 2016 17:34:17 +0200 Subject: [PATCH 091/319] Metrics: 64-bit display fix --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 26ff777b..d50b13b7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10611,7 +10611,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3); ImGui::Text("%d allocations", ImGui::GetIO().MetricsAllocs); - ImGui::Text("sizeof(ImGuiContext) = %u, sizeof(ImGuiWindow) = %u", sizeof(ImGuiContext), sizeof(ImGuiWindow)); + ImGui::Text("sizeof(ImGuiContext) = %u, sizeof(ImGuiWindow) = %u", (int)sizeof(ImGuiContext), (int)sizeof(ImGuiWindow)); static bool show_clip_rects = true; ImGui::Checkbox("Show clipping rectangles when hovering a ImDrawCmd", &show_clip_rects); ImGui::Separator(); From e3fec8c0aaa00e92c0c22570ff7a7df6b3910074 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 20 Aug 2016 19:19:58 +0200 Subject: [PATCH 092/319] Renamed function (#323) --- imgui.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d50b13b7..a26dd9be 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2363,7 +2363,7 @@ static ImVec2 NavCalcPreferredMousePos() return ImClamp(p, r.Min, r.Max); } -static void SetNavIdMoveMouse(ImGuiID id, const ImRect& rect_rel) +static void SetNavIdAndMoveMouse(ImGuiID id, const ImRect& rect_rel) { ImGuiContext& g = *GImGui; g.NavId = id; @@ -2519,7 +2519,7 @@ static void NavUpdate() // Apply result from previous frame navigation directional move request ImGui::SetActiveID(0); - SetNavIdMoveMouse(g.NavMoveResultId, g.NavMoveResultRectRel); + SetNavIdAndMoveMouse(g.NavMoveResultId, g.NavMoveResultRectRel); g.NavMoveFromClampedRefRect = false; } @@ -2590,7 +2590,7 @@ static void NavUpdate() g.NavDisableHighlight = false; g.NavDisableMouseHover = true; if (g.NavLayer == 0 && g.NavWindow->NavLastId) - SetNavIdMoveMouse(g.NavWindow->NavLastId, ImRect()); + SetNavIdAndMoveMouse(g.NavWindow->NavLastId, ImRect()); else NavInitWindow(g.NavWindow, true); } @@ -2632,7 +2632,7 @@ static void NavUpdate() // Leave the "menu" layer g.NavLayer = 0; if (g.NavWindow->NavLastId) - SetNavIdMoveMouse(g.NavWindow->NavLastId, ImRect()); + SetNavIdAndMoveMouse(g.NavWindow->NavLastId, ImRect()); else NavInitWindow(g.NavWindow, true); } From e4c099d67c653f03138b500554a5fba05abf1bc4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 20 Aug 2016 22:11:00 +0200 Subject: [PATCH 093/319] Nav: Comments + fixed handling of PadLeft in menus (fix e55882f) (#323) --- imgui.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a26dd9be..98398001 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -164,9 +164,10 @@ - query focus information with IsWindowFocused(), IsAnyWindowFocused(), IsAnyItemFocused() functions. The reality is more complex than what those flags can express. Please discuss your issues and usage scenario in the thread above. As we head toward more keyboard-oriented development this aspect will need to be improved. - - It is recommended that you enable the 'io.NavMovesMouse' option. Enabling it instructs ImGui that it can request moving your move cursor to track navigated items and ease readability. + - It is recommended that you enable the 'io.NavMovesMouse' option. Enabling it instructs ImGui that it can move your move cursor to track navigated items and ease readability. When enabled and using directional navigation (with d-pad or arrow keys), the NewFrame() functions may alter 'io.MousePos' and set 'io.WantMoveMouse' to notify you that it did so. - When that happens your back-end will need to move the OS mouse cursor on the next frame. The examples binding in examples/ do that. + When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. The examples binding in examples/ do that. + (Important: It you set 'io.NavMovesMouse' to true but don't honor 'io.WantMoveMouse' properly, imgui will misbehave as it will think your mouse is moving back and forth.) // Application init io.NavMovesMouse = true; @@ -9794,7 +9795,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) want_open = true; g.NavMoveRequest = false; } - if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveRequest && g.NavMoveDir == ImGuiNavDir_Left) // Nav-Left to close + if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveRequest && g.NavMoveDir == ImGuiNavDir_Left && IsPopupOpen(id)) // Nav-Left to close { want_close = true; g.NavMoveRequest = false; From 4f7c63a7af393db05e7706140ba04f2a8282e521 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 20 Aug 2016 23:25:53 +0200 Subject: [PATCH 094/319] Nav: Fixed a bug where mouse positioning requests would be sent while opening submenus with mouse (#323) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 98398001..9936ba34 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2463,7 +2463,7 @@ static void NavUpdate() IM_ASSERT(g.NavWindow); g.NavId = g.NavInitDefaultResultId; g.NavRefRectRel = g.NavInitDefaultResultRectRel; - if (!g.NavDisableHighlight) + if (g.NavDisableMouseHover) g.NavMousePosDirty = true; if (g.NavLayer == 0) g.NavWindow->NavLastId = g.NavId; From 695ca7bb45eea9a48b84a8def2d15616aeffdc41 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 20 Aug 2016 23:27:25 +0200 Subject: [PATCH 095/319] Nav: Tidying up. Comments. (#323) --- imgui.cpp | 47 ++++++++++++++++++++++++----------------------- imgui_internal.h | 4 ++-- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9936ba34..9fa3a282 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2333,6 +2333,24 @@ int ImGui::GetFrameCount() return GImGui->FrameCount; } +static void SetNavId(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + g.NavId = id; + if (g.NavLayer == 0) + g.NavWindow->NavLastId = g.NavId; +} + +static void SetNavIdAndMoveMouse(ImGuiID id, const ImRect& rect_rel) +{ + ImGuiContext& g = *GImGui; + SetNavId(id); + g.NavRefRectRel = rect_rel; + g.NavMousePosDirty = true; + g.NavDisableHighlight = false; + g.NavDisableMouseHover = true; +} + // This needs to be called before we submit any widget (aka in or before Begin) static void NavInitWindow(ImGuiWindow* window, bool force_reinit) { @@ -2340,9 +2358,7 @@ static void NavInitWindow(ImGuiWindow* window, bool force_reinit) IM_ASSERT(window == g.NavWindow); if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastId == 0) || force_reinit) { - g.NavId = 0; - if (g.NavLayer == 0) - window->NavLastId = 0; + SetNavId(0); g.NavInitDefaultRequest = true; g.NavInitDefaultResultId = 0; g.NavInitDefaultResultExplicit = false; @@ -2364,18 +2380,6 @@ static ImVec2 NavCalcPreferredMousePos() return ImClamp(p, r.Min, r.Max); } -static void SetNavIdAndMoveMouse(ImGuiID id, const ImRect& rect_rel) -{ - ImGuiContext& g = *GImGui; - g.NavId = id; - if (g.NavLayer == 0) - g.NavWindow->NavLastId = g.NavId; - g.NavRefRectRel = rect_rel; - g.NavMousePosDirty = true; - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; -} - static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N) { ImGuiContext& g = *GImGui; @@ -2461,12 +2465,10 @@ static void NavUpdate() { // Apply result from previous navigation init request (typically select the first item, unless SetItemDefaultFocus() has been called) IM_ASSERT(g.NavWindow); - g.NavId = g.NavInitDefaultResultId; + SetNavId(g.NavInitDefaultResultId); g.NavRefRectRel = g.NavInitDefaultResultRectRel; if (g.NavDisableMouseHover) g.NavMousePosDirty = true; - if (g.NavLayer == 0) - g.NavWindow->NavLastId = g.NavId; } g.NavInitDefaultRequest = false; g.NavInitDefaultResultExplicit = false; @@ -2616,9 +2618,7 @@ static void NavUpdate() ImGuiWindow* child_window = g.NavWindow; ImGuiWindow* parent_window = g.NavWindow->ParentWindow; ImGui::FocusWindow(parent_window); - g.NavId = parent_window->GetChildID(child_window); - if (g.NavLayer == 0) - parent_window->NavLastId = g.NavId; + SetNavId(parent_window->GetChildID(child_window)); g.NavIdIsAlive = false; if (g.NavDisableMouseHover) g.NavMousePosDirty = true; @@ -9756,7 +9756,8 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (!enabled) PopStyleColor(); } - bool hovered = enabled && IsHovered(window->DC.LastItemRect, id); + bool hovered = enabled && IsHovered(window->DC.LastItemRect, id); // FIXME: Why not using window->DC.LastItemHoveredAndUsable / IsItemHovered() ? + if (menuset_is_open) g.NavWindow = backed_nav_window; @@ -10724,7 +10725,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); ImGui::Text("HoveredId: 0x%08X/0x%08X", g.HoveredId, g.HoveredIdPreviousFrame); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not ImGui::Text("ActiveId: 0x%08X/0x%08X, ActiveIdWindow: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); - ImGui::Text("NavWindow: '%s', NavId: 0x%08X", g.NavWindow ? g.NavWindow->Name : "NULL", g.NavId); + ImGui::Text("NavWindow: '%s', NavId: 0x%08X, NavLayer: %d", g.NavWindow ? g.NavWindow->Name : "NULL", g.NavId, g.NavLayer); ImGui::Text("NavRefRectRel: (%.1f,%.1f)(%.1f,%.1f)", g.NavRefRectRel.Min.x, g.NavRefRectRel.Min.y, g.NavRefRectRel.Max.x, g.NavRefRectRel.Max.y); ImGui::Text("NavUsable: %d, NavActive: %d", g.IO.NavUsable, g.IO.NavActive); ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); diff --git a/imgui_internal.h b/imgui_internal.h index 0bbeba41..cabd5601 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -416,8 +416,8 @@ struct ImGuiContext int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid bool NavMousePosDirty; - bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard functionalities - bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we disable mouse hovering until mouse is touched again + bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (nb: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) + bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. bool NavInitDefaultRequest; // Init request for appearing window to select first item ImGuiID NavInitDefaultResultId; ImRect NavInitDefaultResultRectRel; From bf4265785095e4e1a41d90a32200974d5538bf2e Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 20 Aug 2016 23:59:54 +0200 Subject: [PATCH 096/319] Nav: Clearing mouse hover flag using MouseClicked[] test instead of MouseDown[] so that invalid mouse button won't keep breaking nav (#323) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 9fa3a282..8d85c29f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2817,7 +2817,7 @@ void ImGui::NewFrame() { g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i])); } - if (g.IO.MouseDown[i]) // Pressing any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation + if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation g.NavDisableMouseHover = false; } From 30c04d0dbefb89822840b6dc71d43490fb14150c Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 21 Aug 2016 00:10:48 +0200 Subject: [PATCH 097/319] Nav: Mouse pos passed to backend always rounded, so that lossy application of non-integer mouse pos doesn't lead to undesirable movement (#3 --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 8d85c29f..fccc7e2d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2377,7 +2377,7 @@ static ImVec2 NavCalcPreferredMousePos() return g.IO.MousePos; ImVec2 p = g.NavWindow->Pos + ImVec2(g.NavRefRectRel.Min.x + ImMin(g.Style.FramePadding.x*4, g.NavRefRectRel.GetWidth()), g.NavRefRectRel.Max.y - ImMin(g.Style.FramePadding.y, g.NavRefRectRel.GetHeight())); ImRect r = GetVisibleRect(); - return ImClamp(p, r.Min, r.Max); + return ImFloor(ImClamp(p, r.Min, r.Max)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. } static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N) From 43ee5d73e9f524320489720639942ff60bbfaf54 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 21 Aug 2016 11:45:50 +0200 Subject: [PATCH 098/319] Nav: Hovering MenuItem/Selectable with mouse makes NavId, matching what seems to be Windows beahvior at least for menus (#323) --- imgui.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index fccc7e2d..f47a4108 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9484,6 +9484,13 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (flags & ImGuiSelectableFlags_Disabled) selected = false; + // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets) + if (hovered && !g.NavDisableMouseHover && (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)) + { + g.NavDisableHighlight = true; + SetNavId(id); + } + // Render if (hovered || selected) { From ea2425ad9a423425d454991c6446b1f258fa6069 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 21 Aug 2016 12:44:59 +0200 Subject: [PATCH 099/319] Nav: Fixed entering child with PadActivate not setting ActiveIdSource to Nav (#323) --- imgui.cpp | 5 ++++- imgui_internal.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index f47a4108..ae8fb2f6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4321,6 +4321,7 @@ bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, FocusWindow(child_window); NavInitWindow(child_window, false); SetActiveIDNoNav(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item + GImGui->ActiveIdSource = ImGuiInputSource_Nav; } return ret; @@ -10728,10 +10729,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) } if (ImGui::TreeNode("Basic state")) { + const char* input_source_names[] = { "None", "Mouse", "Nav" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_Count_); ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); ImGui::Text("HoveredId: 0x%08X/0x%08X", g.HoveredId, g.HoveredIdPreviousFrame); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not - ImGui::Text("ActiveId: 0x%08X/0x%08X, ActiveIdWindow: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); + ImGui::Text("ActiveId: 0x%08X/0x%08X, ActiveIdSource: %s", g.ActiveId, g.ActiveIdPreviousFrame, input_source_names[g.ActiveIdSource]); + ImGui::Text("ActiveIdWindow: '%s", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); ImGui::Text("NavWindow: '%s', NavId: 0x%08X, NavLayer: %d", g.NavWindow ? g.NavWindow->Name : "NULL", g.NavId, g.NavLayer); ImGui::Text("NavRefRectRel: (%.1f,%.1f)(%.1f,%.1f)", g.NavRefRectRel.Min.x, g.NavRefRectRel.Min.y, g.NavRefRectRel.Max.x, g.NavRefRectRel.Max.y); ImGui::Text("NavUsable: %d, NavActive: %d", g.IO.NavUsable, g.IO.NavActive); diff --git a/imgui_internal.h b/imgui_internal.h index cabd5601..a4bd7bb0 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -203,6 +203,7 @@ enum ImGuiInputSource ImGuiInputSource_None = 0, ImGuiInputSource_Mouse, ImGuiInputSource_Nav, + ImGuiInputSource_Count_, }; enum ImGuiNavDir From 87eb749cbccb302e049f77129180528733e948a9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 21 Aug 2016 13:25:40 +0200 Subject: [PATCH 100/319] Added IsItemHoveredOrFocused() (provisional name), better handling of popup/tooltip positioning when using mouse+nav (#323) --- imgui.cpp | 18 ++++++++++++------ imgui.h | 1 + 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ae8fb2f6..77e1d626 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3961,6 +3961,12 @@ bool ImGui::IsItemFocused() return g.NavId && !g.NavDisableHighlight && g.NavId == g.CurrentWindow->DC.LastItemId; } +bool ImGui::IsItemHoveredOrFocused() +{ + ImGuiContext& g = *GImGui; + return g.NavDisableMouseHover ? IsItemFocused() : IsItemHovered(); +} + bool ImGui::IsItemClicked(int mouse_button) { return IsMouseClicked(mouse_button) && IsItemHovered(); @@ -4090,7 +4096,7 @@ void ImGui::OpenPopupEx(const char* str_id, bool reopen_existing) ImGuiID id = window->GetID(str_id); int current_stack_size = g.CurrentPopupStack.Size; ImVec2 mouse_pos = g.IO.MousePos; - ImVec2 popup_pos = (g.ActiveIdSource == ImGuiInputSource_Nav) ? NavCalcPreferredMousePos() : mouse_pos; + ImVec2 popup_pos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : mouse_pos; ImGuiPopupRef popup_ref = ImGuiPopupRef(id, window, window->GetID("##menus"), popup_pos, mouse_pos); // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here) if (g.OpenPopupStack.Size < current_stack_size + 1) g.OpenPopupStack.push_back(popup_ref); @@ -4820,7 +4826,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us // Position tooltip (always follows mouse) if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api) { - ImVec2 ref_pos = (g.ActiveIdSource == ImGuiInputSource_Nav) ? NavCalcPreferredMousePos() : g.IO.MousePos; + ImVec2 ref_pos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos; ImRect rect_to_avoid(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24, ref_pos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead? window->PosFloat = FindBestPopupWindowPos(ref_pos, window->Size, &window->AutoPosLastDirection, rect_to_avoid); if (window->AutoPosLastDirection == -1) @@ -9876,7 +9882,7 @@ bool ImGui::ColorButton(const ImVec4& col, bool small_height, bool outline_borde RenderNavHighlight(bb, id); RenderFrame(bb.Min, bb.Max, GetColorU32(col), outline_border, style.FrameRounding); - if (g.HoveredId == id) + if (IsItemHoveredOrFocused()) SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col.x, col.y, col.z, col.w, IM_F32_TO_INT8_SAT(col.x), IM_F32_TO_INT8_SAT(col.y), IM_F32_TO_INT8_SAT(col.z), IM_F32_TO_INT8_SAT(col.z)); return pressed; @@ -9994,7 +10000,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], bool alpha) g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away! // Recreate our own tooltip over's ColorButton() one because we want to display correct alpha here - if (IsItemHovered()) + if (IsItemHoveredOrFocused()) SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col[0], col[1], col[2], col[3], IM_F32_TO_INT8_SAT(col[0]), IM_F32_TO_INT8_SAT(col[1]), IM_F32_TO_INT8_SAT(col[2]), IM_F32_TO_INT8_SAT(col[3])); if (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton) @@ -10653,7 +10659,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() || ImGui::IsItemFocused())) + if (show_clip_rects && ImGui::IsItemHoveredOrFocused()) { ImRect clip_rect = pcmd->ClipRect; ImRect vtxs_rect; @@ -10677,7 +10683,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() || ImGui::IsItemFocused()) + if (ImGui::IsItemHoveredOrFocused()) 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(); diff --git a/imgui.h b/imgui.h index 420df52d..02bec568 100644 --- a/imgui.h +++ b/imgui.h @@ -399,6 +399,7 @@ namespace ImGui IMGUI_API bool IsItemHoveredRect(); // is the last item hovered by mouse? even if another item is active or window is blocked by popup while we are hovering this IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited- items that don't interact will always return false) IMGUI_API bool IsItemFocused(); // is the last item focused for keyboard/gamepad navigation? + IMGUI_API bool IsItemHoveredOrFocused(); // select best suitable between IsItemHovered() when mouse is used and IsItemFocused() when navigation is used. useful for tooltips that needs to work with both controls. IMGUI_API bool IsItemClicked(int mouse_button = 0); // is the last item clicked? (e.g. button/node just clicked on) IMGUI_API bool IsItemVisible(); // is the last item visible? (aka not out of sight due to clipping/scrolling.) IMGUI_API bool IsAnyItemHovered(); From ac7826c8a542b3862661c77935b37dbaf5fe410c Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 21 Aug 2016 14:37:48 +0200 Subject: [PATCH 101/319] ImRect: Added Translate() helper, removed redundant and misleading Reduce() --- imgui.cpp | 6 +++--- imgui_internal.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 77e1d626..f150ebc2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4416,7 +4416,7 @@ static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, // Clamp into visible area while not overlapping the cursor. Safety padding is optional if our popup size won't fit without it. ImVec2 safe_padding = style.DisplaySafeAreaPadding; ImRect r_outer(GetVisibleRect()); - r_outer.Reduce(ImVec2((size.x - r_outer.GetWidth() > safe_padding.x*2) ? safe_padding.x : 0.0f, (size.y - r_outer.GetHeight() > safe_padding.y*2) ? safe_padding.y : 0.0f)); + r_outer.Expand(ImVec2((size.x - r_outer.GetWidth() > safe_padding.x*2) ? -safe_padding.x : 0.0f, (size.y - r_outer.GetHeight() > safe_padding.y*2) ? -safe_padding.y : 0.0f)); ImVec2 base_pos_clamped = ImClamp(base_pos, r_outer.Min, r_outer.Max - size); for (int n = (*last_dir != -1) ? -1 : 0; n < 4; n++) // Last, Right, down, up, left. (Favor last used direction). @@ -5234,7 +5234,7 @@ static void Scrollbar(ImGuiWindow* window, bool horizontal) else window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? 2 : 0) | (other_scrollbar ? 0 : 4); window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_ScrollbarBg), window_rounding, window_rounding_corners); - bb.Reduce(ImVec2(ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f))); + bb.Expand(ImVec2(-ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f))); // V denote the main axis of the scrollbar float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight(); @@ -8104,7 +8104,7 @@ void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* over // Render fraction = ImSaturate(fraction); RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - bb.Reduce(ImVec2(window->BorderSize, window->BorderSize)); + bb.Expand(ImVec2(-window->BorderSize, -window->BorderSize)); const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y); RenderFrame(bb.Min, fill_br, GetColorU32(ImGuiCol_PlotHistogram), false, style.FrameRounding); diff --git a/imgui_internal.h b/imgui_internal.h index a4bd7bb0..210f6b05 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -242,7 +242,7 @@ struct IMGUI_API ImRect void Add(const ImRect& rhs) { if (Min.x > rhs.Min.x) Min.x = rhs.Min.x; if (Min.y > rhs.Min.y) Min.y = rhs.Min.y; if (Max.x < rhs.Max.x) Max.x = rhs.Max.x; if (Max.y < rhs.Max.y) Max.y = rhs.Max.y; } void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; } void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; } - void Reduce(const ImVec2& amount) { Min.x += amount.x; Min.y += amount.y; Max.x -= amount.x; Max.y -= amount.y; } + void Translate(const ImVec2& v) { Min.x += v.x; Min.y += v.y; Max.x += v.x; Max.y += v.y; } void Clip(ImRect& r) const { r.Min.x = ImClamp(r.Min.x, Min.x, Max.x); r.Min.y = ImClamp(r.Min.y, Min.y, Max.y); r.Max.x = ImClamp(r.Max.x, Min.x, Max.x); r.Max.y = ImClamp(r.Max.y, Min.y, Max.y); } void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; } ImVec2 GetClosestPoint(ImVec2 p, bool on_edge) const From f71cdd13b71895c1d7f10b7eaa9d0fdc2849f626 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 21 Aug 2016 14:39:13 +0200 Subject: [PATCH 102/319] Internal tidying up, moved code to CalcNextScrollFromScrollTargetAndClamp() so it can be reused by upcoming nav code. --- imgui.cpp | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f150ebc2..e2718ce5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -710,6 +710,7 @@ static inline bool IsWindowContentHoverable(ImGuiWindow* window); static void ClearSetNextWindowData(); static void CheckStacksSize(ImGuiWindow* window, bool write); static void Scrollbar(ImGuiWindow* window, bool horizontal); +static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window); static void AddDrawListToRenderList(ImVector& out_render_list, ImDrawList* draw_list); static void AddWindowToRenderList(ImVector& out_render_list, ImGuiWindow* window); @@ -4539,6 +4540,19 @@ static void ApplySizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size) window->SizeFull = new_size; } +static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) +{ + ImVec2 scroll = window->Scroll; + if (window->ScrollTarget.x < FLT_MAX) + scroll.x = window->ScrollTarget.x - (window->ScrollTargetCenterRatio.x * window->SizeFull.x); + if (window->ScrollTarget.y < FLT_MAX) + scroll.y = window->ScrollTarget.y - ((1.0f - window->ScrollTargetCenterRatio.y) * (window->TitleBarHeight() + window->MenuBarHeight())) - (window->ScrollTargetCenterRatio.y * window->SizeFull.y); + scroll = ImMax(scroll, ImVec2(0.0f, 0.0f)); + if (!window->Collapsed && !window->SkipItems) + scroll = ImMin(scroll, ImMax(ImVec2(0.0f, 0.0f), window->SizeContents - window->SizeFull + window->ScrollbarSizes)); + return scroll; +} + // Push a new ImGui window to add widgets to. // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. // - Begin/End can be called multiple times during the frame with the same window name to append content. @@ -4858,21 +4872,8 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX; // Apply scrolling - if (window->ScrollTarget.x < FLT_MAX) - { - float center_ratio = window->ScrollTargetCenterRatio.x; - window->Scroll.x = window->ScrollTarget.x - (center_ratio * window->SizeFull.x); - window->ScrollTarget.x = FLT_MAX; - } - if (window->ScrollTarget.y < FLT_MAX) - { - float center_ratio = window->ScrollTargetCenterRatio.y; - window->Scroll.y = window->ScrollTarget.y - ((1.0f - center_ratio) * (window->TitleBarHeight() + window->MenuBarHeight())) - (center_ratio * window->SizeFull.y); - window->ScrollTarget.y = FLT_MAX; - } - window->Scroll = ImMax(window->Scroll, ImVec2(0.0f, 0.0f)); - if (!window->Collapsed && !window->SkipItems) - window->Scroll = ImMin(window->Scroll, ImMax(ImVec2(0.0f, 0.0f), window->SizeContents - window->SizeFull + window->ScrollbarSizes)); + window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window); + window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); // Modal window darkens what is behind them if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow()) From 78b7e2dfb3aaf5cef13da1fbe04de7b81fe9cdf4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 21 Aug 2016 14:53:26 +0200 Subject: [PATCH 103/319] Nav: Process and apply mouse move request immediately after move request result comes (#323) --- imgui.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e2718ce5..5fcb2097 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2475,20 +2475,6 @@ static void NavUpdate() g.NavInitDefaultResultExplicit = false; g.NavInitDefaultResultId = 0; - // Apply application mouse position movement - if (g.NavMousePosDirty && g.NavIdIsAlive) - { - // Set mouse position given our knowledge of the nav widget position from last frame - if (g.IO.NavMovesMouse) - { - g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredMousePos(); - g.IO.WantMoveMouse = true; - } - g.NavMousePosDirty = false; - } - g.NavIdIsAlive = false; - g.NavTabbedId = 0; - // Process navigation move request if (g.NavMoveRequest && g.NavMoveResultId != 0) { @@ -2519,6 +2505,10 @@ static void NavUpdate() g.NavWindow->ScrollTarget.y = g.NavMoveResultRectRel.Max.y + g.NavWindow->Scroll.y + g.Style.ItemSpacing.y; g.NavWindow->ScrollTargetCenterRatio.y = 1.0f; } + + // Estimate upcoming scroll so we can offset our relative mouse position so mouse position can be applied immediately (under this block) + ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(g.NavWindow); + g.NavMoveResultRectRel.Translate(g.NavWindow->Scroll - next_scroll); } // Apply result from previous frame navigation directional move request @@ -2527,6 +2517,20 @@ static void NavUpdate() g.NavMoveFromClampedRefRect = false; } + // Apply application mouse position movement, after we had a chance to process move request result. + if (g.NavMousePosDirty && g.NavIdIsAlive) + { + // Set mouse position given our knowledge of the nav widget position from last frame + if (g.IO.NavMovesMouse) + { + g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredMousePos(); + g.IO.WantMoveMouse = true; + } + g.NavMousePosDirty = false; + } + g.NavIdIsAlive = false; + g.NavTabbedId = 0; + // Navigation windowing mode (change focus, move/resize window) if (!g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiNavReadMode_Pressed)) { From fddf9ca10ed63f8d837a3104ac178d1910bdcf93 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 21 Aug 2016 15:25:29 +0200 Subject: [PATCH 104/319] Nav: Fixed nav highlight clipping (affected non non-menu items within menubar) (#323) --- imgui.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5fcb2097..f2d08b29 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2084,12 +2084,14 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id) return; ImGuiWindow* window = ImGui::GetCurrentWindow(); - ImRect clip_rect(window->InnerRect.Min - ImVec2(4,4), window->InnerRect.Max + ImVec2(4,4)); - if (!window->ClipRect.Contains(clip_rect)) - window->DrawList->PushClipRect(clip_rect.Min, clip_rect.Max); - window->DrawList->AddRect(bb.Min - ImVec2(3,3), bb.Max + ImVec2(3,3), GetColorU32(ImGuiCol_NavHighlight), g.Style.FrameRounding, 0x0F, 2.0f); + const float THICKNESS = 2.0f; + const float DISTANCE = 3.0f + THICKNESS * 0.5f; + ImRect display_rect(bb.Min - ImVec2(DISTANCE,DISTANCE), bb.Max + ImVec2(DISTANCE,DISTANCE)); + if (!window->ClipRect.Contains(display_rect)) + window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); + window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), g.Style.FrameRounding, 0x0F, THICKNESS); //window->DrawList->AddRect(g.NavRefRectScreen.Min, g.NavRefRectScreen.Max, IM_COL32(255,0,0,255)); - if (!window->ClipRect.Contains(clip_rect)) + if (!window->ClipRect.Contains(display_rect)) window->DrawList->PopClipRect(); } From 252f0941014a53e52eaade05cef3968858914020 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 21 Aug 2016 15:26:40 +0200 Subject: [PATCH 105/319] Nav: Undo 87eb749cbc, agressively including nav focus test in IsItemHovered() (#323) --- imgui.cpp | 17 +++++++---------- imgui.h | 3 +-- imgui_demo.cpp | 2 +- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f2d08b29..f6a39491 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3942,6 +3942,9 @@ void ImGui::CaptureMouseFromApp(bool capture) bool ImGui::IsItemHovered() { ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiContext& g = *GImGui; + if (g.NavDisableMouseHover) + return IsItemFocused(); return window->DC.LastItemHoveredAndUsable; } @@ -3968,12 +3971,6 @@ bool ImGui::IsItemFocused() return g.NavId && !g.NavDisableHighlight && g.NavId == g.CurrentWindow->DC.LastItemId; } -bool ImGui::IsItemHoveredOrFocused() -{ - ImGuiContext& g = *GImGui; - return g.NavDisableMouseHover ? IsItemFocused() : IsItemHovered(); -} - bool ImGui::IsItemClicked(int mouse_button) { return IsMouseClicked(mouse_button) && IsItemHovered(); @@ -9889,7 +9886,7 @@ bool ImGui::ColorButton(const ImVec4& col, bool small_height, bool outline_borde RenderNavHighlight(bb, id); RenderFrame(bb.Min, bb.Max, GetColorU32(col), outline_border, style.FrameRounding); - if (IsItemHoveredOrFocused()) + if (IsItemHovered()) SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col.x, col.y, col.z, col.w, IM_F32_TO_INT8_SAT(col.x), IM_F32_TO_INT8_SAT(col.y), IM_F32_TO_INT8_SAT(col.z), IM_F32_TO_INT8_SAT(col.z)); return pressed; @@ -10007,7 +10004,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], bool alpha) g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away! // Recreate our own tooltip over's ColorButton() one because we want to display correct alpha here - if (IsItemHoveredOrFocused()) + if (IsItemHovered()) SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col[0], col[1], col[2], col[3], IM_F32_TO_INT8_SAT(col[0]), IM_F32_TO_INT8_SAT(col[1]), IM_F32_TO_INT8_SAT(col[2]), IM_F32_TO_INT8_SAT(col[3])); if (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton) @@ -10666,7 +10663,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::IsItemHoveredOrFocused()) + if (show_clip_rects && ImGui::IsItemHovered()) { ImRect clip_rect = pcmd->ClipRect; ImRect vtxs_rect; @@ -10690,7 +10687,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::IsItemHoveredOrFocused()) + if (ImGui::IsItemHovered()) 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(); diff --git a/imgui.h b/imgui.h index 02bec568..dfb0e5ce 100644 --- a/imgui.h +++ b/imgui.h @@ -395,11 +395,10 @@ namespace ImGui IMGUI_API void PopClipRect(); // Utilities - IMGUI_API bool IsItemHovered(); // is the last item hovered by mouse, and usable? + IMGUI_API bool IsItemHovered(); // is the last item hovered by mouse (and usable)? or we are currently using Nav and the item is focused. IMGUI_API bool IsItemHoveredRect(); // is the last item hovered by mouse? even if another item is active or window is blocked by popup while we are hovering this IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited- items that don't interact will always return false) IMGUI_API bool IsItemFocused(); // is the last item focused for keyboard/gamepad navigation? - IMGUI_API bool IsItemHoveredOrFocused(); // select best suitable between IsItemHovered() when mouse is used and IsItemFocused() when navigation is used. useful for tooltips that needs to work with both controls. IMGUI_API bool IsItemClicked(int mouse_button = 0); // is the last item clicked? (e.g. button/node just clicked on) IMGUI_API bool IsItemVisible(); // is the last item visible? (aka not out of sight due to clipping/scrolling.) IMGUI_API bool IsAnyItemHovered(); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ff946e06..6dc663b3 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -763,7 +763,7 @@ void ImGui::ShowTestWindow(bool* p_open) } } - if (ImGui::CollapsingHeader("Graphs widgets")) + if (ImGui::CollapsingHeader("Plots widgets")) { static bool animate = true; ImGui::Checkbox("Animate", &animate); From 2303b67c40a7ab2a45fc680403d37b1178ed94d3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 21 Aug 2016 16:05:19 +0200 Subject: [PATCH 106/319] Close button submitted after collapse button. Should have no effect. Consistent with expectation & will be used as fallback for nav (#323) --- imgui.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f6a39491..4bb89688 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5088,15 +5088,6 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->DC.ItemFlags &= ~ImGuiItemFlags_AllowNavDefaultFocus; window->DC.NavLayerCurrent++; - // Close button - if (p_open != NULL) - { - const float PAD = 2.0f; - const float rad = (window->TitleBarHeight() - PAD*2.0f) * 0.5f; - if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-PAD - rad, PAD + rad), rad)) - *p_open = false; - } - // Collapse button const ImVec2 text_size = CalcTextSize(name, NULL, true); if (!(flags & ImGuiWindowFlags_NoCollapse)) @@ -5110,6 +5101,15 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us RenderCollapseTriangle(window->Pos + style.FramePadding, !window->Collapsed, 1.0f, true); } + // Close button + if (p_open != NULL) + { + const float PAD = 2.0f; + const float rad = (window->TitleBarHeight() - PAD*2.0f) * 0.5f; + if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-PAD - rad, PAD + rad), rad)) + *p_open = false; + } + window->DC.NavLayerCurrent--; window->DC.ItemFlags = backup_item_options; From 49ca1c2b885f7c41734bfd51e15981bd02545cae Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 21 Aug 2016 16:07:55 +0200 Subject: [PATCH 107/319] Nav: InitDefaultRequest saves a fallback item so PadMenu on a collapsed window gets us to the collapse button (#323) --- imgui.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4bb89688..55c99442 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2116,15 +2116,20 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar // We could early out with `if (is_clipped && !g.NavInitDefaultRequest) return false;` but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) // A more pragmatic solution for handling last lists is relying on the fact that they are likely evenly spread items (so that clipper can work) and we could nav at higher-level (apply index, etc.) // So eventually we would like to provide the user will the primitives to be able to implement that sort of customized/efficient navigation handling whenever necessary. - if (id != NULL && g.NavWindow == window->RootNavWindow && g.IO.NavUsable) + if (id != NULL && g.IO.NavUsable && g.NavWindow == window->RootNavWindow) { const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; const ImRect nav_bb_rel(nav_bb.Min - g.NavWindow->Pos, nav_bb.Max - g.NavWindow->Pos); - if (g.NavInitDefaultRequest && g.NavLayer == window->DC.NavLayerCurrent && (window->DC.ItemFlags & ImGuiItemFlags_AllowNavDefaultFocus)) + if (g.NavInitDefaultRequest && g.NavLayer == window->DC.NavLayerCurrent) { - g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = false; // Clear flag immediately, first item gets default, also simplify the if() in ItemAdd() - g.NavInitDefaultResultId = *id; - g.NavInitDefaultResultRectRel = nav_bb_rel; + // Even if 'ImGuiItemFlags_AllowNavDefaultFocus' is off (typically collapse/close button) we record the first ResultId so they can be used as fallback + if (window->DC.ItemFlags & ImGuiItemFlags_AllowNavDefaultFocus) + g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = false; // Found a match, clear request + if (g.NavInitDefaultResultId == 0 || (window->DC.ItemFlags & ImGuiItemFlags_AllowNavDefaultFocus)) + { + g.NavInitDefaultResultId = *id; + g.NavInitDefaultResultRectRel = nav_bb_rel; + } } //const bool DEBUG_NAV = false; // [DEBUG] Enable to test scoring on all items. From c6c7371a4c8a58dcb28f967dc861dec66bb04823 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 21 Aug 2016 16:11:53 +0200 Subject: [PATCH 108/319] Nav: Removed unnecessary test following 49ca1c2b885f7c41734bfd51e15981bd02545cae (#323) --- imgui.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 55c99442..f28523b7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5089,8 +5089,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us { // Close & collapse button are on layer 1 (same as menus) and don't default focus const ImGuiItemFlags backup_item_options = window->DC.ItemFlags; - if (window->Flags & ImGuiWindowFlags_MenuBar) - window->DC.ItemFlags &= ~ImGuiItemFlags_AllowNavDefaultFocus; + window->DC.ItemFlags &= ~ImGuiItemFlags_AllowNavDefaultFocus; window->DC.NavLayerCurrent++; // Collapse button From df9bdf38f97a2564069881876db88e514cc750c7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 21 Aug 2016 17:32:15 +0200 Subject: [PATCH 109/319] Nav: NavActivateId, NavInputId are no repeat actions. Repeat buttons handle it themselves already anyway. (#323) --- imgui.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f28523b7..f34e319f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -18,6 +18,9 @@ - MISSION STATEMENT - END-USER GUIDE - PROGRAMMER GUIDE (read me!) + - Read first + - Getting started with integrating imgui in your code/engine + - Using gamepad/keyboard navigation [beta] - API BREAKING CHANGES (read me when you update!) - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS - How can I help? @@ -88,7 +91,7 @@ - Call and read ImGui::ShowTestWindow() for demo code demonstrating most features. - Customization: PushStyleColor()/PushStyleVar() or the style editor to tweak the look of the interface (e.g. if you want a more compact UI or a different color scheme). - GETTING STARTED WITH INTEGRATING IN YOUR ENGINE + GETTING STARTED WITH INTEGRATING IMGUI IN YOUR CODE/ENGINE - See examples/ folder for standalone sample applications. Prefer reading examples/opengl_example/ first as it is the simplest. You may be able to grab and copy a ready made imgui_impl_*** file from the examples/. @@ -2658,8 +2661,8 @@ static void NavUpdate() } } - g.NavActivateId = (g.NavId && !g.NavDisableHighlight && !g.NavWindowingTarget && g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadActivate, ImGuiNavReadMode_Repeat)) ? g.NavId : 0; - g.NavInputId = (g.NavId && !g.NavDisableHighlight && !g.NavWindowingTarget && g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadInput, ImGuiNavReadMode_Repeat)) ? g.NavId : 0; + g.NavActivateId = (g.NavId && !g.NavDisableHighlight && !g.NavWindowingTarget && g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadActivate, ImGuiNavReadMode_Pressed)) ? g.NavId : 0; + g.NavInputId = (g.NavId && !g.NavDisableHighlight && !g.NavWindowingTarget && g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadInput, ImGuiNavReadMode_Pressed)) ? g.NavId : 0; if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { g.NavActivateId = g.NavInputId = 0; From 3883a2027f77304cb2d72ea7495789420e567b41 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 21 Aug 2016 17:52:42 +0200 Subject: [PATCH 110/319] Nav: Fixed Selectable() crash introduced earlier today in 43ee5d73 + added comments/assert (#323) --- imgui.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f34e319f..f528568f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2347,6 +2347,7 @@ int ImGui::GetFrameCount() static void SetNavId(ImGuiID id) { ImGuiContext& g = *GImGui; + IM_ASSERT(g.NavWindow); g.NavId = id; if (g.NavLayer == 0) g.NavWindow->NavLastId = g.NavId; @@ -9503,7 +9504,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl selected = false; // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets) - if (hovered && !g.NavDisableMouseHover && (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)) + if (hovered && !g.NavDisableMouseHover && g.NavWindow == window && (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)) { g.NavDisableHighlight = true; SetNavId(id); @@ -9756,7 +9757,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentMenuSet == window->GetID("##menus")); ImGuiWindow* backed_nav_window = g.NavWindow; if (menuset_is_open) - g.NavWindow = window; + g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) ImVec2 popup_pos, pos = window->DC.CursorPos; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) From e8e5c6d788149449c22abe739d35ce311d2a01a0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 23 Aug 2016 08:58:51 +0200 Subject: [PATCH 111/319] Nav: Comments. Fixed two -Wall warnings. Removed unused function. (#787) --- imgui.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f528568f..791c8399 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -154,7 +154,7 @@ USING GAMEPAD/KEYBOARD NAVIGATION [BETA] - Gamepad/keyboard navigation support is available, currently in Beta with some issues. Your feedback and bug reports are welcome. - - See https://github.com/ocornut/imgui/issues/323 for discussion thread and ask questions. + - See https://github.com/ocornut/imgui/issues/787 discussion thread and ask questions there. - The current primary focus is to support game controllers. - Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. - Consider using Synergy host (on your computer) + uSynergy.c (in your console/tablet/phone app) to use PC mouse/keyboard. @@ -624,7 +624,6 @@ - filters: fuzzy matches (may use code at blog.forrestthewoods.com/4cffeed33fdb) - shortcuts: add a shortcut api, e.g. parse "&Save" and/or "Save (CTRL+S)", pass in to widgets or provide simple ways to use (button=activate, input=focus) !- keyboard: tooltip & combo boxes are messing up / not honoring keyboard tabbing - - keyboard: full keyboard navigation and focus. (#323) - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622) - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame) - input: rework IO system to be able to pass actual ordered/timestamped events. (~#335, #71) @@ -697,7 +696,6 @@ static void LogRenderedText(const ImVec2& ref_pos, const char* text, static void PushMultiItemsWidths(int components, float w_full = 0.0f); static float GetDraggedColumnOffset(int column_index); -static bool IsKeyDownMap(ImGuiKey key); static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true); static void SetCurrentFont(ImFont* font); @@ -2956,10 +2954,12 @@ void ImGui::NewFrame() // Pressing TAB activate widget focus //// NB: Don't discard FocusedWindow if it isn't active, so that a window that go on/off programatically won't lose its keyboard focus. // [2016/07/17] That comment was made invalid by 19d02becef94e8e0f1d432a8bd55cd783876583c if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false)) + { if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); else g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0; + } g.NavIdTabCounter = INT_MAX; // Mark all windows as not visible @@ -3781,12 +3781,6 @@ bool ImGui::IsAnyWindowHoveredAtPos(const ImVec2& pos) return FindHoveredWindow(pos, false) != NULL; } -static bool IsKeyDownMap(ImGuiKey key) -{ - const int key_index = GImGui->IO.KeyMap[key]; - return (key_index >= 0) ? ImGui::IsKeyDown(key_index) : false; -} - static bool IsKeyPressedMap(ImGuiKey key, bool repeat) { const int key_index = GImGui->IO.KeyMap[key]; From 67c6d6026a14b70ed619da79b19b3d2565fc0c05 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 27 Nov 2016 18:29:38 +0100 Subject: [PATCH 112/319] Nav: SliderFloat() Fixed non-linear sliders in Nav branch (#787, #323) --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 68bd0ad2..fffedf2e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7339,7 +7339,7 @@ float ImGui::RoundScalar(float value, int decimal_precision) static inline float SliderBehaviorCalcRatioFromValue(float v, float v_min, float v_max, float power, float linear_zero_pos) { - const bool is_non_linear = (power < 1.0f-0.00001f) && (power > 1.0f-0.00001f); + const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f); const float v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min); if (is_non_linear) { @@ -7370,7 +7370,7 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v RenderNavHighlight(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); - const bool is_non_linear = (power < 1.0f-0.00001f) && (power > 1.0f-0.00001f); + const bool is_non_linear = (power < 1.0f-0.00001f) || (power > 1.0f+0.00001f); const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0; const float grab_padding = 2.0f; From bb4d19ff7cbd1daf293b07036dff1a049d7e7bee Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 23 Dec 2016 12:15:09 +0100 Subject: [PATCH 113/319] Merge branch 'master' - merge fix --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index f2242252..cb5da0cf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2634,7 +2634,7 @@ static void NavUpdate() 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; g.NavDisableMouseHover = true; - MarkSettingsDirty(g.NavWindowingTarget); + MarkIniSettingsDirty(g.NavWindowingTarget); } } From 6c82af2b965cb61fb490efe5689d79a4044d9b85 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 22 Aug 2017 17:44:49 +0800 Subject: [PATCH 114/319] Navigation branch fix (we changed Clip) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index f00be193..aaad044b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11066,7 +11066,7 @@ void ImGui::BeginColumns(const char* id, int columns_count, ImGuiColumnsFlags fl float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(column_index) - 1.0f); float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(column_index + 1) - 1.0f); window->DC.ColumnsData[column_index].ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX); - window->DC.ColumnsData[column_index].ClipRect.Clip(window->ClipRect); + window->ClipRect.Clip(window->DC.ColumnsData[column_index].ClipRect); } window->DrawList->ChannelsSplit(window->DC.ColumnsCount); From 444792f75f0a8a9d77e4bfd55b90c5fd79b652e5 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 22 Aug 2017 18:27:23 +0800 Subject: [PATCH 115/319] Merge fixes from ,master branch --- imgui_demo.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ef65a2f5..b8e61278 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1000,8 +1000,7 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::NextColumn(); char buf[32]; sprintf(buf, "%08x", i*5731); - if (ImGui::Button(buf, ImVec2(-1.0f, 0.0f))) - printf("Pressed '%s'\n", buf); + ImGui::Button(buf, ImVec2(-1.0f, 0.0f)); } ImGui::EndChild(); ImGui::PopStyleVar(); @@ -1469,7 +1468,7 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::OpenPopup("Stacked 2"); if (ImGui::BeginPopupModal("Stacked 2")) { - ImGui::Text("Hello from Stacked The Second!\nWe are piling a modal over another here,\nand also testing if the menu-bar works."); + ImGui::Text("Hello from Stacked The Second!"); if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); From bea06117bb1c37ce806bfa533a9109c300e10ed4 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 22 Aug 2017 19:26:21 +0800 Subject: [PATCH 116/319] Nav: Fix navigation inside child windows. Removed GetChildID() and storin/g the info within the window. (#1291) --- imgui.cpp | 19 +++++++------------ imgui_internal.h | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 10503194..5a694791 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1807,6 +1807,7 @@ ImGuiWindow::ImGuiWindow(const char* name) SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f); WindowPadding = ImVec2(0.0f, 0.0f); MoveId = GetID("#MOVE"); + ChildId = 0; Scroll = ImVec2(0.0f, 0.0f); ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); @@ -1877,13 +1878,6 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) return ImHash(str, str_end ? (int)(str_end - str) : 0, seed); } -ImGuiID ImGuiWindow::GetChildID(ImGuiWindow* child_window) -{ - IM_ASSERT(child_window && child_window->ParentWindow == this); - ImGuiID seed = IDStack[0]; - return ImHash(&child_window->ID, sizeof(child_window->ID), seed); -} - //----------------------------------------------------------------------------- // Internal API exposed in imgui_internal.h //----------------------------------------------------------------------------- @@ -2670,7 +2664,8 @@ static void NavUpdate() ImGuiWindow* child_window = g.NavWindow; ImGuiWindow* parent_window = g.NavWindow->ParentWindow; ImGui::FocusWindow(parent_window); - SetNavId(parent_window->GetChildID(child_window)); + IM_ASSERT(child_window->ChildId != 0); + SetNavId(child_window->ChildId); g.NavIdIsAlive = false; if (g.NavDisableMouseHover) g.NavMousePosDirty = true; @@ -4392,12 +4387,12 @@ static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b bool ret = ImGui::Begin(title, NULL, size, -1.0f, flags); ImGuiWindow* child_window = ImGui::GetCurrentWindow(); + child_window->ChildId = id; child_window->AutoFitChildAxises = auto_fit_axises; if (!(parent_window->Flags & ImGuiWindowFlags_ShowBorders)) child_window->Flags &= ~ImGuiWindowFlags_ShowBorders; // Process navigation-in immediately so NavInit can run on first frame - //const ImGuiID id = parent_window->GetChildID(child_window); if (/*!(flags & ImGuiWindowFlags_NavFlattened) &&*/ (child_window->DC.NavLayerActiveFlags != 0 || child_window->DC.NavHasScroll) && GImGui->NavActivateId == id) { ImGui::FocusWindow(child_window); @@ -4440,13 +4435,12 @@ void ImGui::EndChild() ImGui::End(); ImGuiWindow* parent_window = GetCurrentWindow(); - ImGuiID id = parent_window->GetChildID(window); ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); ItemSize(sz); if (/*!(window->Flags & ImGuiWindowFlags_NavFlattened) &&*/ (window->DC.NavLayerActiveFlags != 0 || window->DC.NavHasScroll)) { - ItemAdd(bb, &id); - RenderNavHighlight(bb, id); + ItemAdd(bb, &window->ChildId); + RenderNavHighlight(bb, window->ChildId); } else { @@ -11465,6 +11459,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("NavWindow: '%s', NavId: 0x%08X, NavLayer: %d", g.NavWindow ? g.NavWindow->Name : "NULL", g.NavId, g.NavLayer); ImGui::Text("NavRefRectRel: (%.1f,%.1f)(%.1f,%.1f)", g.NavRefRectRel.Min.x, g.NavRefRectRel.Min.y, g.NavRefRectRel.Max.x, g.NavRefRectRel.Max.y); ImGui::Text("NavUsable: %d, NavActive: %d", g.IO.NavUsable, g.IO.NavActive); + ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); ImGui::TreePop(); } diff --git a/imgui_internal.h b/imgui_internal.h index 5b1b3149..f9e388ec 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -735,6 +735,7 @@ struct IMGUI_API ImGuiWindow ImRect ContentsRegionRect; // Maximum visible content position in window coordinates. ~~ (SizeContentsExplicit ? SizeContentsExplicit : Size - ScrollbarSizes) - CursorStartPos, per axis ImVec2 WindowPadding; // Window padding at the time of begin. We need to lock it, in particular manipulation of the ShowBorder would have an effect ImGuiID MoveId; // == window->GetID("#MOVE") + ImGuiID ChildId; // Id of corresponding item in parent window (for child windows) ImVec2 Scroll; ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change) ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered @@ -792,7 +793,6 @@ public: ImGuiID GetID(const char* str, const char* str_end = NULL); ImGuiID GetID(const void* ptr); ImGuiID GetIDNoKeepAlive(const char* str, const char* str_end = NULL); - ImGuiID GetChildID(ImGuiWindow* window); ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x+Size.x, Pos.y+Size.y); } float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale; } From f3ab5e6252ec5483fe82c6fd243cbb0caac94ff1 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 22 Aug 2017 19:43:49 +0800 Subject: [PATCH 117/319] Fixed InputText() bug with ImGuiInputTextFlags_EnterReturnsTrue (in nav branch only) (#787). Thanks @Grouflon --- imgui.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 5a694791..acefc457 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8932,7 +8932,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 value_changed = true; } } - if (!cancel_edit && !clear_active_id) + + // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame. + // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. Also this allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage. + bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0); + if (apply_edit_back_to_user_buffer) { // Apply new value immediately - copy modified buffer back // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer From 2e35957a81d4ff67b30ea80c1592682de796d325 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 22 Aug 2017 20:02:52 +0800 Subject: [PATCH 118/319] Undo reordering of ImGuiKey in Nav branch --- imgui.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.h b/imgui.h index d5ed61b5..eae153b9 100644 --- a/imgui.h +++ b/imgui.h @@ -587,10 +587,10 @@ enum ImGuiSelectableFlags_ enum ImGuiKey_ { ImGuiKey_Tab, // for tabbing through fields - ImGuiKey_UpArrow, // for text edit - ImGuiKey_DownArrow, // for text edit ImGuiKey_LeftArrow, // for text edit ImGuiKey_RightArrow,// for text edit + ImGuiKey_UpArrow, // for text edit + ImGuiKey_DownArrow, // for text edit ImGuiKey_PageUp, ImGuiKey_PageDown, ImGuiKey_Home, // for text edit From b0fc30bd199636d5f94e59a07c7b0846b964e1f3 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 22 Aug 2017 20:49:02 +0800 Subject: [PATCH 119/319] Merge branch 'master' into navigation --- imgui.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index bb5799c9..2ef0b6b0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5725,7 +5725,8 @@ bool ImGui::IsWindowHovered() bool ImGui::IsWindowRectHovered() { - return GImGui->HoveredWindow == GImGui->CurrentWindow; + ImGuiContext& g = *GImGui; + return g.HoveredWindow == g.CurrentWindow; } bool ImGui::IsWindowFocused() @@ -7786,6 +7787,7 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s if (v_speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX) v_speed = (v_max - v_min) * g.DragSpeedDefaultRatio; + if (g.ActiveIdSource == ImGuiInputSource_Nav) v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); From e6ed2f99397d7906c61410d0819ec5e3a2b192ac Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 25 Aug 2017 00:06:57 +0800 Subject: [PATCH 120/319] Nav: Tooltip do not appear in navigation focus list. (#1294, #787) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 2ef0b6b0..89a8c406 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4107,7 +4107,7 @@ static void BeginTooltipEx(bool override_previous_tooltip) window->HiddenFrames = 1; ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip%02d", ++g.TooltipOverrideCount); } - ImGui::Begin(window_name, NULL, ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize); + ImGui::Begin(window_name, NULL, ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoNavFocus); } void ImGui::SetTooltipV(const char* fmt, va_list args) From 6752cba4bc5662cc0186e54616fcec53340e726c Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 29 Aug 2017 15:39:11 +0800 Subject: [PATCH 121/319] Removed duplicate comments --- imgui.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index fd8bc25e..a6c46011 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -261,9 +261,6 @@ - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it. - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc. - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully breakage should be minimal. - - 2016/07/18 (1.50) - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete). - - renamed IsPosHoveringAnyWindow() to IsAnyWindowHoveredAtPos() for consistency. Kept inline redirection function (will obsolete). - - renamed IsMouseHoveringWindow() to IsWindowHoveredRect() for consistency. Kept inline redirection function (will obsolete). - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you. However if your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar. From 97a40e74fb902a27473aa6b033d711ccd247949d Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 29 Aug 2017 18:45:58 +0800 Subject: [PATCH 122/319] Nav: Fixed bad merge of 6def01be5df16e17d71f81a8d4083a0f48d04a8d so the bug fixed in #840 wasn't fully fixed in Nav branch. --- imgui.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0dcafa2c..c0f0c4b7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1919,6 +1919,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) g.ActiveIdWindow = window; if (id) { + g.ActiveIdIsAlive = true; g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavTabbedId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; if (g.ActiveIdSource == ImGuiInputSource_Nav) g.NavDisableMouseHover = true; @@ -1939,9 +1940,12 @@ void ImGui::SetActiveIDNoNav(ImGuiID id, ImGuiWindow* window) g.ActiveId = id; g.ActiveIdAllowNavDirFlags = 0; g.ActiveIdAllowOverlap = false; - g.ActiveIdIsAlive |= (id != 0); g.ActiveIdWindow = window; - g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; + if (id) + { + g.ActiveIdIsAlive = true; + g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; + } } void ImGui::ClearActiveID() From 6c91a1ef7fabbcb39d2188d57c947e66e9d79de8 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 17 Sep 2017 23:35:39 +0200 Subject: [PATCH 123/319] Minor comments --- imgui.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3dd3d3f0..425f0d75 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2034,9 +2034,9 @@ static bool NavScoreItem(ImRect cand) } // Compute distance between boxes - // FIXME-NAVIGATION: Introducing various biases toward typical imgui uses cases, but we don't have any rigorous proof of their effect now. + // FIXME-NAVIGATION: Introducing various biases toward typical imgui uses cases, but we don't have any rigorous proof of their side-effect.. float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); - float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Clamp down on Y to keep using box-distance for vertically touching items + float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items if (dby && dbx) dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f); float dist_box = fabsf(dbx) + fabsf(dby); @@ -2160,8 +2160,8 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests unfortunately, but it is still limited to one window. // it may not scale very well for windows with ten of thousands of item, but at least the NavRequest is only performed on user interaction, aka maximum once a frame. // We could early out with `if (is_clipped && !g.NavInitDefaultRequest) return false;` but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) - // A more pragmatic solution for handling last lists is relying on the fact that they are likely evenly spread items (so that clipper can work) and we could nav at higher-level (apply index, etc.) - // So eventually we would like to provide the user will the primitives to be able to implement that sort of customized/efficient navigation handling whenever necessary. + // A more pragmatic solution for handling long lists is relying on the fact that they are likely evenly spread items (so that clipper can be used) and we could Nav at higher-level (apply index, etc.) + // So eventually we would like to provide the user will the primitives to be able to implement that customized/efficient navigation handling whenever necessary. if (id != NULL && g.IO.NavUsable && g.NavWindow == window->RootNavWindow) { const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; From 113b2467cdb8c8cbf380c4a49e1cd5d747f3fc81 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 20 Sep 2017 23:02:06 +0200 Subject: [PATCH 124/319] Minor bits. Reduce usage of GImGui multiple times in same function. --- imgui.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 097b049e..cc420b0f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -825,8 +825,8 @@ ImGuiIO::ImGuiIO() ImeWindowHandle = NULL; // Input (NB: we already have memset zero the entire structure) - MousePos = ImVec2(-FLT_MAX,-FLT_MAX); - MousePosPrev = ImVec2(-FLT_MAX,-FLT_MAX); + MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); MouseDragThreshold = 6.0f; for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f; @@ -4058,17 +4058,20 @@ bool ImGui::IsItemClicked(int mouse_button) bool ImGui::IsAnyItemHovered() { - return GImGui->HoveredId != 0 || GImGui->HoveredIdPreviousFrame != 0; + ImGuiContext& g = *GImGui; + return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0; } bool ImGui::IsAnyItemActive() { - return GImGui->ActiveId != 0; + ImGuiContext& g = *GImGui; + return g.ActiveId != 0; } bool ImGui::IsAnyItemFocused() { - return GImGui->NavId != 0 && !GImGui->NavDisableHighlight; + ImGuiContext& g = *GImGui; + return g.NavId != 0 && !g.NavDisableHighlight; } bool ImGui::IsItemVisible() @@ -4406,6 +4409,7 @@ bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button) static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) { + ImGuiContext& g = *GImGui; ImGuiWindow* parent_window = ImGui::GetCurrentWindow(); ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow; @@ -4434,12 +4438,12 @@ static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b child_window->Flags &= ~ImGuiWindowFlags_ShowBorders; // Process navigation-in immediately so NavInit can run on first frame - if (/*!(flags & ImGuiWindowFlags_NavFlattened) &&*/ (child_window->DC.NavLayerActiveFlags != 0 || child_window->DC.NavHasScroll) && GImGui->NavActivateId == id) + if (/*!(flags & ImGuiWindowFlags_NavFlattened) &&*/ (child_window->DC.NavLayerActiveFlags != 0 || child_window->DC.NavHasScroll) && g.NavActivateId == id) { ImGui::FocusWindow(child_window); NavInitWindow(child_window, false); ImGui::SetActiveIDNoNav(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item - GImGui->ActiveIdSource = ImGuiInputSource_Nav; + g.ActiveIdSource = ImGuiInputSource_Nav; } return ret; From b9c185402ef6ee8524c9ebd115e45d8967e20c77 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 20 Sep 2017 23:58:56 +0200 Subject: [PATCH 125/319] Nav: minor tidying up NavUpdate() to use a local variables, easier to test replacing g.NavWindow with g NavMoveResultWindow for navigation accross flattened child windows. --- imgui.cpp | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cc420b0f..ca4bde2c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2545,36 +2545,37 @@ static void NavUpdate() if (g.NavMoveRequest && g.NavMoveResultId != 0) { IM_ASSERT(g.NavWindow); + ImGuiWindow* window = g.NavWindow; // Scroll to keep newly navigated item fully into view - 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] + ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1,1), window->InnerRect.Max - window->Pos + ImVec2(1,1)); + //g.OverlayDrawList.AddRect(window->Pos + window_rect_rel.Min, window->Pos + window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG] if (g.NavLayer == 0 && !window_rect_rel.Contains(g.NavMoveResultRectRel)) { - if (g.NavWindow->ScrollbarX && g.NavMoveResultRectRel.Min.x < window_rect_rel.Min.x) + if (window->ScrollbarX && g.NavMoveResultRectRel.Min.x < window_rect_rel.Min.x) { - g.NavWindow->ScrollTarget.x = g.NavMoveResultRectRel.Min.x + g.NavWindow->Scroll.x - g.Style.ItemSpacing.x; - g.NavWindow->ScrollTargetCenterRatio.x = 0.0f; + window->ScrollTarget.x = g.NavMoveResultRectRel.Min.x + window->Scroll.x - g.Style.ItemSpacing.x; + window->ScrollTargetCenterRatio.x = 0.0f; } - else if (g.NavWindow->ScrollbarX && g.NavMoveResultRectRel.Max.x >= window_rect_rel.Max.x) + else if (window->ScrollbarX && g.NavMoveResultRectRel.Max.x >= window_rect_rel.Max.x) { - g.NavWindow->ScrollTarget.x = g.NavMoveResultRectRel.Max.x + g.NavWindow->Scroll.x + g.Style.ItemSpacing.x; - g.NavWindow->ScrollTargetCenterRatio.x = 1.0f; + window->ScrollTarget.x = g.NavMoveResultRectRel.Max.x + window->Scroll.x + g.Style.ItemSpacing.x; + window->ScrollTargetCenterRatio.x = 1.0f; } if (g.NavMoveResultRectRel.Min.y < window_rect_rel.Min.y) { - g.NavWindow->ScrollTarget.y = g.NavMoveResultRectRel.Min.y + g.NavWindow->Scroll.y - g.Style.ItemSpacing.y; - g.NavWindow->ScrollTargetCenterRatio.y = 0.0f; + window->ScrollTarget.y = g.NavMoveResultRectRel.Min.y + window->Scroll.y - g.Style.ItemSpacing.y; + window->ScrollTargetCenterRatio.y = 0.0f; } else if (g.NavMoveResultRectRel.Max.y >= window_rect_rel.Max.y) { - g.NavWindow->ScrollTarget.y = g.NavMoveResultRectRel.Max.y + g.NavWindow->Scroll.y + g.Style.ItemSpacing.y; - g.NavWindow->ScrollTargetCenterRatio.y = 1.0f; + window->ScrollTarget.y = g.NavMoveResultRectRel.Max.y + window->Scroll.y + g.Style.ItemSpacing.y; + window->ScrollTargetCenterRatio.y = 1.0f; } // Estimate upcoming scroll so we can offset our relative mouse position so mouse position can be applied immediately (under this block) - ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(g.NavWindow); - g.NavMoveResultRectRel.Translate(g.NavWindow->Scroll - next_scroll); + ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); + g.NavMoveResultRectRel.Translate(window->Scroll - next_scroll); } // Apply result from previous frame navigation directional move request From 869732c456619af9e5eeb883b6f1e692d8b9c44c Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 27 Sep 2017 12:34:18 +0200 Subject: [PATCH 126/319] Nav: Removed unnecessary combo code (that kept the combo arrow highlighted after reverting to mouse controls). (#787) --- imgui.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6362f62a..01606b1c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9538,14 +9538,13 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImVec2 popu bool hovered, held; bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held); - const bool navigated = g.NavId == id; bool popup_open = IsPopupOpen(id); const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f)); RenderNavHighlight(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_open || hovered || navigated ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING + RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_open || hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING RenderCollapseTriangle(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), true); if (preview_value != NULL) From 79ef64430c1f7089c96a41fe1b2abce0a5836900 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 27 Sep 2017 17:27:21 +0200 Subject: [PATCH 127/319] Nav: Fixed merge cc26db8ec7f87a21b705fa9821a3c9f76003c7e5 --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 0e33fc4d..f6199c7d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2226,7 +2226,7 @@ bool ImGui::IsItemHovered() if (g.HoveredWindow == window) if (g.ActiveId == 0 || g.ActiveId == window->DC.LastItemId || g.ActiveIdAllowOverlap || g.ActiveId == window->MoveId) if (IsMouseHoveringRect(window->DC.LastItemRect.Min, window->DC.LastItemRect.Max)) - if (IsWindowContentHoverable(window)) + if (!g.NavDisableMouseHover && IsWindowContentHoverable(window)) return true; return false; From 2a8eb618dcc9443a6b741f22b3cf0b0ee038e8b2 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Sep 2017 00:36:58 +0200 Subject: [PATCH 128/319] Nav: Fixed NavDown to open menu from a menu bar (#787) --- imgui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.cpp b/imgui.cpp index 44088aad..43ffbde9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10039,6 +10039,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Menu bar: Nav-Down to open { g.NavMoveRequest = false; + want_open = true; } if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' From af38749ea10b665ee2651f21e7253cf5c9e829df Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Sep 2017 19:07:52 +0200 Subject: [PATCH 129/319] Nav: Fixed ButtonBehavior mistakenly setting active id when the Activate button is held and we have a new NavId, which affected browsing some popups (#787) --- imgui.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index caf621ff..56feaa3a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6575,12 +6575,16 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool hovered = true; if (!g.NavWindowingTarget && IsNavInputDown(ImGuiNavInput_PadActivate)) { - // Set active id so it can be queried by user via IsItemActive(), etc. but don't react to it ourselves - g.NavActivateId = id; - SetActiveID(id, window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - if (IsNavInputPressed(ImGuiNavInput_PadActivate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiNavReadMode_Repeat : ImGuiNavReadMode_Pressed)) + bool nav_pressed = IsNavInputPressed(ImGuiNavInput_PadActivate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiNavReadMode_Repeat : ImGuiNavReadMode_Pressed); + if (nav_pressed) pressed = true; + if (nav_pressed || g.ActiveId == id) + { + // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. + g.NavActivateId = id; // This is so SetActiveId assign a Nav source + SetActiveID(id, window); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); + } } } From e0a2a832cd69c2d285333a1e5190f4adc664fffd Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Sep 2017 19:18:41 +0200 Subject: [PATCH 130/319] Nav: Changed internal flag to NoNavDefaultFocus to be false by allow, and more consistent (#787) --- imgui.cpp | 6 +++--- imgui_internal.h | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 56feaa3a..750b5e50 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2189,9 +2189,9 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar if (g.NavInitDefaultRequest && g.NavLayer == window->DC.NavLayerCurrent) { // Even if 'ImGuiItemFlags_AllowNavDefaultFocus' is off (typically collapse/close button) we record the first ResultId so they can be used as fallback - if (window->DC.ItemFlags & ImGuiItemFlags_AllowNavDefaultFocus) + if (!(window->DC.ItemFlags & ImGuiItemFlags_NoNavDefaultFocus)) g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = false; // Found a match, clear request - if (g.NavInitDefaultResultId == 0 || (window->DC.ItemFlags & ImGuiItemFlags_AllowNavDefaultFocus)) + if (g.NavInitDefaultResultId == 0 || !(window->DC.ItemFlags & ImGuiItemFlags_NoNavDefaultFocus)) { g.NavInitDefaultResultId = *id; g.NavInitDefaultResultRectRel = nav_bb_rel; @@ -5242,7 +5242,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us { // Close & collapse button are on layer 1 (same as menus) and don't default focus const ImGuiItemFlags backup_item_options = window->DC.ItemFlags; - window->DC.ItemFlags &= ~ImGuiItemFlags_AllowNavDefaultFocus; + window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; window->DC.NavLayerCurrent++; // Collapse button diff --git a/imgui_internal.h b/imgui_internal.h index b604262c..2aa49baa 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -167,6 +167,7 @@ inline void operator delete(void*, ImPlacementNewDummy, void*) {} // Types //----------------------------------------------------------------------------- +// NB: Most of those flags are handled by ButtonBehavior(), but some as for the higher level ButtonEx() function only. enum ImGuiButtonFlags_ { ImGuiButtonFlags_Repeat = 1 << 0, // hold to repeat @@ -637,9 +638,9 @@ enum ImGuiItemFlags_ ImGuiItemFlags_AllowKeyboardFocus = 1 << 0, // true ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. //ImGuiItemFlags_Disabled = 1 << 2, // false // All widgets appears are disabled - ImGuiItemFlags_AllowNavDefaultFocus = 1 << 3, // true + ImGuiItemFlags_NoNavDefaultFocus = 1 << 3, // true ImGuiItemFlags_SelectableDontClosePopup = 1 << 4, // false // MenuItem/Selectable() automatically closes current Popup window - ImGuiItemFlags_Default_ = ImGuiItemFlags_AllowKeyboardFocus|ImGuiItemFlags_AllowNavDefaultFocus + ImGuiItemFlags_Default_ = ImGuiItemFlags_AllowKeyboardFocus }; // Transient per-window data, reset at the beginning of the frame From 50ba5437439395a2c415cbedbc3788f8d2c22dac Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Sep 2017 19:35:10 +0200 Subject: [PATCH 131/319] Comments --- imgui_internal.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 2aa49baa..bbe98874 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -171,13 +171,13 @@ inline void operator delete(void*, ImPlacementNewDummy, void*) {} enum ImGuiButtonFlags_ { ImGuiButtonFlags_Repeat = 1 << 0, // hold to repeat - ImGuiButtonFlags_PressedOnClickRelease = 1 << 1, // (default) return pressed on click+release on same item (default if no PressedOn** flag is set) - ImGuiButtonFlags_PressedOnClick = 1 << 2, // return pressed on click (default requires click+release) - ImGuiButtonFlags_PressedOnRelease = 1 << 3, // return pressed on release (default requires click+release) - ImGuiButtonFlags_PressedOnDoubleClick = 1 << 4, // return pressed on double-click (default requires click+release) - ImGuiButtonFlags_FlattenChilds = 1 << 5, // allow interaction even if a child window is overlapping - ImGuiButtonFlags_DontClosePopups = 1 << 6, // disable automatically closing parent popup on press - ImGuiButtonFlags_Disabled = 1 << 7, // disable interaction + ImGuiButtonFlags_PressedOnClickRelease = 1 << 1, // return true on click + release on same item [DEFAULT if no PressedOn* flag is set] + ImGuiButtonFlags_PressedOnClick = 1 << 2, // return true on click (default requires click+release) + ImGuiButtonFlags_PressedOnRelease = 1 << 3, // return true on release (default requires click+release) + ImGuiButtonFlags_PressedOnDoubleClick = 1 << 4, // return true on double-click (default requires click+release) + ImGuiButtonFlags_FlattenChilds = 1 << 5, // allow interactions even if a child window is overlapping + ImGuiButtonFlags_DontClosePopups = 1 << 6, // disable automatically closing parent popup on press // [UNUSED] + ImGuiButtonFlags_Disabled = 1 << 7, // disable interactions ImGuiButtonFlags_AlignTextBaseLine = 1 << 8, // vertically align button to match text baseline - ButtonEx() only ImGuiButtonFlags_NoKeyModifiers = 1 << 9, // disable interaction if a key modifier is held ImGuiButtonFlags_AllowOverlapMode = 1 << 10, // require previous frame HoveredId to either match id or be null before being usable From 4b4e455c40a340310f06cab45ceddb957d68ace4 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Sep 2017 19:41:30 +0200 Subject: [PATCH 132/319] Nav: Internal nenaming --- imgui.cpp | 20 ++++++++++---------- imgui_internal.h | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 750b5e50..2a566284 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2173,7 +2173,7 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar window->DC.LastItemRect = bb; window->DC.LastItemRectHoveredRect = false; if (id != NULL) - window->DC.NavLayerActiveFlagsNext |= (1 << window->DC.NavLayerCurrent); + window->DC.NavLayerActiveMaskNext |= (1 << window->DC.NavLayerCurrent); // Navigation processing runs prior to clipping early-out // (a) So that NavInitDefaultRequest can be honored, for newly opened windows to select a default widget @@ -2188,8 +2188,8 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar const ImRect nav_bb_rel(nav_bb.Min - g.NavWindow->Pos, nav_bb.Max - g.NavWindow->Pos); if (g.NavInitDefaultRequest && g.NavLayer == window->DC.NavLayerCurrent) { - // Even if 'ImGuiItemFlags_AllowNavDefaultFocus' is off (typically collapse/close button) we record the first ResultId so they can be used as fallback if (!(window->DC.ItemFlags & ImGuiItemFlags_NoNavDefaultFocus)) + // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = false; // Found a match, clear request if (g.NavInitDefaultResultId == 0 || !(window->DC.ItemFlags & ImGuiItemFlags_NoNavDefaultFocus)) { @@ -2695,9 +2695,9 @@ static void NavUpdate() // Single press toggles NavLayer if (g.NavWindowingToggleLayer && g.NavWindow) { - if ((g.NavWindow->DC.NavLayerActiveFlags & (1<<1)) == 0 && (g.NavWindow->RootWindow->DC.NavLayerActiveFlags & (1<<1)) != 0) + if ((g.NavWindow->DC.NavLayerActiveMask & (1<<1)) == 0 && (g.NavWindow->RootWindow->DC.NavLayerActiveMask & (1<<1)) != 0) ImGui::FocusWindow(g.NavWindow->RootWindow); - g.NavLayer = (g.NavWindow->DC.NavLayerActiveFlags & (1<<1)) ? (g.NavLayer ^ 1) : 0; + g.NavLayer = (g.NavWindow->DC.NavLayerActiveMask & (1<<1)) ? (g.NavLayer ^ 1) : 0; g.NavDisableHighlight = false; g.NavDisableMouseHover = true; if (g.NavLayer == 0 && g.NavWindow->NavLastId) @@ -2790,7 +2790,7 @@ static void NavUpdate() { // Fallback manual-scroll with NavUp/NavDown when window has no navigable item const float scroll_speed = ImFloor(g.NavWindow->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. - if (!g.NavWindow->DC.NavLayerActiveFlags && g.NavWindow->DC.NavHasScroll && g.NavMoveRequest && (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)) + if (!g.NavWindow->DC.NavLayerActiveMask && g.NavWindow->DC.NavHasScroll && g.NavMoveRequest && (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)) SetWindowScrollY(g.NavWindow, ImFloor(g.NavWindow->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); // Manual scroll with NavScrollXXX keys @@ -4462,7 +4462,7 @@ static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b child_window->Flags &= ~ImGuiWindowFlags_ShowBorders; // Process navigation-in immediately so NavInit can run on first frame - if (/*!(flags & ImGuiWindowFlags_NavFlattened) &&*/ (child_window->DC.NavLayerActiveFlags != 0 || child_window->DC.NavHasScroll) && g.NavActivateId == id) + if (/*!(flags & ImGuiWindowFlags_NavFlattened) &&*/ (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll) && g.NavActivateId == id) { ImGui::FocusWindow(child_window); NavInitWindow(child_window, false); @@ -4506,7 +4506,7 @@ void ImGui::EndChild() ImGuiWindow* parent_window = GetCurrentWindow(); ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); ItemSize(sz); - if (/*!(window->Flags & ImGuiWindowFlags_NavFlattened) &&*/ (window->DC.NavLayerActiveFlags != 0 || window->DC.NavHasScroll)) + if (/*!(window->Flags & ImGuiWindowFlags_NavFlattened) &&*/ (window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll)) { ItemAdd(bb, &window->ChildId); RenderNavHighlight(bb, window->ChildId); @@ -5194,8 +5194,8 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->DC.CursorMaxPos = window->DC.CursorStartPos; window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f; window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; - window->DC.NavLayerActiveFlags = window->DC.NavLayerActiveFlagsNext; - window->DC.NavLayerActiveFlagsNext = 0x00; + window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext; + window->DC.NavLayerActiveMaskNext = 0x00; window->DC.NavHasScroll = (GetScrollMaxY() > 0.0f); window->DC.MenuBarAppending = false; window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x); @@ -11585,7 +11585,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y); ImGui::BulletText("Active: %d, Accessed: %d", window->Active, window->Accessed); - ImGui::BulletText("NavLastId: 0x%08x, NavLayerActiveFlags: %02X", window->NavLastId, window->DC.NavLayerActiveFlags); + ImGui::BulletText("NavLastId: 0x%08x, NavLayerActiveMask: %02X", window->NavLastId, window->DC.NavLayerActiveMask); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); diff --git a/imgui_internal.h b/imgui_internal.h index bbe98874..940c6ff7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -662,7 +662,7 @@ struct IMGUI_API ImGuiDrawContext bool LastItemRectHoveredRect; bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) int NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) - int NavLayerActiveFlags, NavLayerActiveFlagsNext; // Which layer have been written to. + int NavLayerActiveMask, NavLayerActiveMaskNext; // Which layer have been written to. bool MenuBarAppending; float MenuBarOffsetX; ImVector ChildWindows; @@ -705,7 +705,7 @@ struct IMGUI_API ImGuiDrawContext LastItemRect = ImRect(); LastItemRectHoveredRect = false; NavHasScroll = false; - NavLayerActiveFlags = NavLayerActiveFlagsNext = 0x00; + NavLayerActiveMask = NavLayerActiveMaskNext = 0x00; NavLayerCurrent = 0; MenuBarAppending = false; MenuBarOffsetX = 0.0f; From dd0855de5c26a82407eb0ec3412193fe5ec585ac Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Sep 2017 19:47:45 +0200 Subject: [PATCH 133/319] ButtonBehavior: Tidying up. --- imgui.cpp | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2a566284..2dc87ddc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6589,25 +6589,30 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool } bool held = false; - if (g.ActiveId == id && g.ActiveIdSource == ImGuiInputSource_Mouse) + if (g.ActiveId == id) { - if (g.IO.MouseDown[0]) + if (g.ActiveIdSource == ImGuiInputSource_Mouse) { - held = true; + if (g.IO.MouseDown[0]) + { + held = true; + } + else + { + if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease)) + if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps + pressed = true; + ClearActiveID(); + } + if (!(flags & ImGuiButtonFlags_NoNavOverride)) + g.NavDisableHighlight = true; } - else + else if (g.ActiveIdSource == ImGuiInputSource_Nav) { - if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease)) - if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps - pressed = true; - ClearActiveID(); + if (!IsNavInputDown(ImGuiNavInput_PadActivate)) + ClearActiveID(); } - if (!(flags & ImGuiButtonFlags_NoNavOverride)) - g.NavDisableHighlight = true; } - if (g.ActiveId == id && g.ActiveIdSource == ImGuiInputSource_Nav) - if (!IsNavInputDown(ImGuiNavInput_PadActivate)) - ClearActiveID(); // 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 (hovered && (flags & ImGuiButtonFlags_AllowOverlapMode) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0)) From 878fa96896da94c80dcbf0ce588adfff8cae270f Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Sep 2017 19:57:24 +0200 Subject: [PATCH 134/319] Nav: Re-arranged ItemAdd() to maximize early out (#787) --- imgui.cpp | 67 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2dc87ddc..e79a6f46 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2182,41 +2182,44 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar // We could early out with `if (is_clipped && !g.NavInitDefaultRequest) return false;` but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) // A more pragmatic solution for handling long lists is relying on the fact that they are likely evenly spread items (so that clipper can be used) and we could Nav at higher-level (apply index, etc.) // So eventually we would like to provide the user will the primitives to be able to implement that customized/efficient navigation handling whenever necessary. - if (id != NULL && g.IO.NavUsable && g.NavWindow == window->RootNavWindow) - { - const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; - const ImRect nav_bb_rel(nav_bb.Min - g.NavWindow->Pos, nav_bb.Max - g.NavWindow->Pos); - if (g.NavInitDefaultRequest && g.NavLayer == window->DC.NavLayerCurrent) - { - if (!(window->DC.ItemFlags & ImGuiItemFlags_NoNavDefaultFocus)) - // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback - g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = false; // Found a match, clear request - if (g.NavInitDefaultResultId == 0 || !(window->DC.ItemFlags & ImGuiItemFlags_NoNavDefaultFocus)) + if (id != NULL && g.NavWindow == window->RootNavWindow) + if (g.NavId == *id || g.NavMoveRequest || g.NavInitDefaultRequest) + if (g.IO.NavUsable) { - g.NavInitDefaultResultId = *id; - g.NavInitDefaultResultRectRel = nav_bb_rel; - } - } + const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; + const ImRect nav_bb_rel(nav_bb.Min - g.NavWindow->Pos, nav_bb.Max - g.NavWindow->Pos); + if (g.NavInitDefaultRequest && g.NavLayer == window->DC.NavLayerCurrent) + { + // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback + const ImGuiItemFlags item_flags = window->DC.ItemFlags; + if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) + g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = false; // Found a match, clear request + if (g.NavInitDefaultResultId == 0 || !(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) + { + g.NavInitDefaultResultId = *id; + g.NavInitDefaultResultRectRel = nav_bb_rel; + } + } - //const bool DEBUG_NAV = false; // [DEBUG] Enable to test scoring on all items. - if ((g.NavMoveRequest /*|| DEBUG_NAV*/) && g.NavId != *id) - { - //if (DEBUG_NAV && !g.NavMoveRequest) g.NavMoveDir = ImGuiDir_N; - if (NavScoreItem(nav_bb)) //if (!DEBUG || g.NavMoveRequest) - { - g.NavMoveResultId = *id; - g.NavMoveResultRectRel = nav_bb_rel; - } - } + //const bool DEBUG_NAV = false; // [DEBUG] Enable to test scoring on all items. + if ((g.NavMoveRequest /*|| DEBUG_NAV*/) && g.NavId != *id) + { + //if (DEBUG_NAV && !g.NavMoveRequest) g.NavMoveDir = ImGuiDir_N; + if (NavScoreItem(nav_bb)) //if (!DEBUG || g.NavMoveRequest) + { + g.NavMoveResultId = *id; + g.NavMoveResultRectRel = nav_bb_rel; + } + } - // Update window-relative bounding box of navigated item - if (g.NavId == *id) - { - g.NavRefRectRel = nav_bb_rel; - g.NavIdIsAlive = true; - g.NavIdTabCounter = window->FocusIdxTabCounter; - } - } + // Update window-relative bounding box of navigated item + if (g.NavId == *id) + { + g.NavRefRectRel = nav_bb_rel; + g.NavIdIsAlive = true; + g.NavIdTabCounter = window->FocusIdxTabCounter; + } + } if (is_clipped) return false; From 2d859dee4eb2ee6fff922e63d8dea545ed4e6ebe Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Sep 2017 19:58:01 +0200 Subject: [PATCH 135/319] Nav: Added ImGuiItemFlags_NoNav item flag --- imgui.cpp | 4 ++-- imgui_internal.h | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e79a6f46..2e04738d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2182,16 +2182,16 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar // We could early out with `if (is_clipped && !g.NavInitDefaultRequest) return false;` but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) // A more pragmatic solution for handling long lists is relying on the fact that they are likely evenly spread items (so that clipper can be used) and we could Nav at higher-level (apply index, etc.) // So eventually we would like to provide the user will the primitives to be able to implement that customized/efficient navigation handling whenever necessary. + const ImGuiItemFlags item_flags = window->DC.ItemFlags; if (id != NULL && g.NavWindow == window->RootNavWindow) if (g.NavId == *id || g.NavMoveRequest || g.NavInitDefaultRequest) - if (g.IO.NavUsable) + if (g.IO.NavUsable && !(item_flags & ImGuiItemFlags_NoNav)) { const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; const ImRect nav_bb_rel(nav_bb.Min - g.NavWindow->Pos, nav_bb.Max - g.NavWindow->Pos); if (g.NavInitDefaultRequest && g.NavLayer == window->DC.NavLayerCurrent) { // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback - const ImGuiItemFlags item_flags = window->DC.ItemFlags; if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = false; // Found a match, clear request if (g.NavInitDefaultResultId == 0 || !(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) diff --git a/imgui_internal.h b/imgui_internal.h index 940c6ff7..5b59b688 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -638,8 +638,9 @@ enum ImGuiItemFlags_ ImGuiItemFlags_AllowKeyboardFocus = 1 << 0, // true ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. //ImGuiItemFlags_Disabled = 1 << 2, // false // All widgets appears are disabled - ImGuiItemFlags_NoNavDefaultFocus = 1 << 3, // true - ImGuiItemFlags_SelectableDontClosePopup = 1 << 4, // false // MenuItem/Selectable() automatically closes current Popup window + ImGuiItemFlags_NoNav = 1 << 3, // false + ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false + ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window ImGuiItemFlags_Default_ = ImGuiItemFlags_AllowKeyboardFocus }; From 7c2926de17ad16032e5d4ea0f1865305ecccfbdd Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Sep 2017 20:15:32 +0200 Subject: [PATCH 136/319] Demo: Added an extra test related to baseline and fixed an id collision. --- imgui_demo.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3fbf4624..b7e77a79 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -811,7 +811,7 @@ void ImGui::ShowTestWindow(bool* p_open) } ImGui::Text("Color button only:"); - ImGui::ColorButton("MyColor##3b", *(ImVec4*)&color, misc_flags, ImVec2(80,80)); + ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, misc_flags, ImVec2(80,80)); ImGui::Text("Color picker:"); static bool alpha = true; @@ -1186,7 +1186,8 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::Text("Text aligned to Widget"); ImGui::SameLine(); ImGui::Button("Widget##1"); ImGui::SameLine(); ImGui::Text("Widget"); ImGui::SameLine(); - ImGui::SmallButton("Widget##2"); + ImGui::SmallButton("Widget##2"); ImGui::SameLine(); + ImGui::Button("Widget##3"); // Tree const float spacing = ImGui::GetStyle().ItemInnerSpacing.x; From df366b230e0aecf9bfab1402f5154854d7dd829a Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Sep 2017 20:22:30 +0200 Subject: [PATCH 137/319] ColorPicker4: Use nav flag to provide a slighly better experience when using navigation (#787, #346) --- imgui.cpp | 10 +++++++--- imgui_internal.h | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2e04738d..5e4945e0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6648,7 +6648,8 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags if (!ItemAdd(bb, &id)) return false; - if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) flags |= ImGuiButtonFlags_Repeat; + if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) + flags |= ImGuiButtonFlags_Repeat; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); @@ -10598,6 +10599,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl bool value_changed = false, value_changed_h = false, value_changed_sv = false; + PushItemFlag(ImGuiItemFlags_NoNav, true); if (flags & ImGuiColorEditFlags_PickerHueWheel) { // Hue wheel + SV triangle logic @@ -10655,10 +10657,9 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl value_changed = value_changed_h = true; } } - - // Alpha bar logic if (alpha_bar) { + // Alpha bar logic SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y)); InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size)); if (IsItemActive()) @@ -10667,6 +10668,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl value_changed = true; } } + PopItemFlag(); // ImGuiItemFlags_NoNav if (!(flags & ImGuiColorEditFlags_NoSidePreview)) { @@ -10687,6 +10689,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl if (!(flags & ImGuiColorEditFlags_NoSidePreview)) { + PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); if ((flags & ImGuiColorEditFlags_NoLabel)) Text("Current"); @@ -10701,6 +10704,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl value_changed = true; } } + PopItemFlag(); EndGroup(); } diff --git a/imgui_internal.h b/imgui_internal.h index 5b59b688..e2920a47 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -178,7 +178,7 @@ enum ImGuiButtonFlags_ ImGuiButtonFlags_FlattenChilds = 1 << 5, // allow interactions even if a child window is overlapping ImGuiButtonFlags_DontClosePopups = 1 << 6, // disable automatically closing parent popup on press // [UNUSED] ImGuiButtonFlags_Disabled = 1 << 7, // disable interactions - ImGuiButtonFlags_AlignTextBaseLine = 1 << 8, // vertically align button to match text baseline - ButtonEx() only + ImGuiButtonFlags_AlignTextBaseLine = 1 << 8, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled SmallButton(), not possible currently because of DC.CursorPosPrevLine ImGuiButtonFlags_NoKeyModifiers = 1 << 9, // disable interaction if a key modifier is held ImGuiButtonFlags_AllowOverlapMode = 1 << 10, // require previous frame HoveredId to either match id or be null before being usable ImGuiButtonFlags_NoNavOverride = 1 << 11 // don't override navigation id when activated From 1f7f54e196e550d563a0f18e80ac173d3810e8e8 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Sep 2017 22:00:17 +0200 Subject: [PATCH 138/319] Removed extraneous test. --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 5e4945e0..dada4887 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4987,7 +4987,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us IM_ASSERT(window_pos_set_by_api); float horizontal_overlap = style.ItemSpacing.x; // We want some overlap to convey the relative depth of each popup (currently the amount of overlap it is hard-coded to style.ItemSpacing.x, may need to introduce another style value). ImRect rect_to_avoid; - if (parent_window && parent_window->DC.MenuBarAppending) + if (parent_window->DC.MenuBarAppending) rect_to_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight()); else rect_to_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX); From 30b1d85962814ce3d127e17dcc697dafba45fd10 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Sep 2017 23:48:30 +0200 Subject: [PATCH 139/319] Nav: Commiting some better organized Debug helper because this going to stay for a bit. --- imgui.cpp | 31 +++++++++++++++++++++---------- imgui_internal.h | 3 ++- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index dada4887..fa632ee6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -594,6 +594,8 @@ #include // intptr_t #endif +#define IMGUI_DEBUG_NAV 0 + #ifdef _MSC_VER #pragma warning (disable: 4127) // condition expression is constant #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) @@ -2091,7 +2093,7 @@ static bool NavScoreItem(ImRect cand) quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; } -#if 0 // [DEBUG] +#if IMGUI_DEBUG_NAV if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) { char buf[128]; @@ -2184,7 +2186,7 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar // So eventually we would like to provide the user will the primitives to be able to implement that customized/efficient navigation handling whenever necessary. const ImGuiItemFlags item_flags = window->DC.ItemFlags; if (id != NULL && g.NavWindow == window->RootNavWindow) - if (g.NavId == *id || g.NavMoveRequest || g.NavInitDefaultRequest) + if (g.NavId == *id || g.NavMoveRequest || g.NavInitDefaultRequest || IMGUI_DEBUG_NAV) if (g.IO.NavUsable && !(item_flags & ImGuiItemFlags_NoNav)) { const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; @@ -2201,15 +2203,21 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar } } - //const bool DEBUG_NAV = false; // [DEBUG] Enable to test scoring on all items. - if ((g.NavMoveRequest /*|| DEBUG_NAV*/) && g.NavId != *id) + bool new_best = false; +#if IMGUI_DEBUG_NAV + // [DEBUG] Score items at all times + if (!g.NavMoveRequest) + g.NavMoveDir = g.NavMoveDirLast; + if (g.NavId != *id) + new_best = NavScoreItem(nav_bb) && g.NavMoveRequest; +#else + if (g.NavMoveRequest && g.NavId != *id) + new_best = NavScoreItem(nav_bb); +#endif + if (new_best) { - //if (DEBUG_NAV && !g.NavMoveRequest) g.NavMoveDir = ImGuiDir_N; - if (NavScoreItem(nav_bb)) //if (!DEBUG || g.NavMoveRequest) - { - g.NavMoveResultId = *id; - g.NavMoveResultRectRel = nav_bb_rel; - } + g.NavMoveResultId = *id; + g.NavMoveResultRectRel = nav_bb_rel; } // Update window-relative bounding box of navigated item @@ -2778,7 +2786,10 @@ static void NavUpdate() if ((allowed_dir_flags & (1< Date: Fri, 29 Sep 2017 00:01:26 +0200 Subject: [PATCH 140/319] Nav: Support for fallback horizontal scrolling with PadLeft/PadRight (nb: fallback scrolling is only used to navigate windows that have no interactive items). (#787) --- imgui.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index fa632ee6..186f262c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2802,12 +2802,17 @@ static void NavUpdate() // Scrolling if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) { - // Fallback manual-scroll with NavUp/NavDown when window has no navigable item + // *Fallback* manual-scroll with NavUp/NavDown when window has no navigable item const float scroll_speed = ImFloor(g.NavWindow->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. - if (!g.NavWindow->DC.NavLayerActiveMask && g.NavWindow->DC.NavHasScroll && g.NavMoveRequest && (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)) - SetWindowScrollY(g.NavWindow, ImFloor(g.NavWindow->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); + if (!g.NavWindow->DC.NavLayerActiveMask && g.NavWindow->DC.NavHasScroll && g.NavMoveRequest) + { + if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) + SetWindowScrollX(g.NavWindow, ImFloor(g.NavWindow->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); + if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) + SetWindowScrollY(g.NavWindow, ImFloor(g.NavWindow->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); + } - // Manual scroll with NavScrollXXX keys + // *Normal* Manual scroll with NavScrollXXX keys ImVec2 scroll_dir = GetNavInputAmount2d(1, ImGuiNavReadMode_Down, 1.0f/10.0f, 10.0f); if (scroll_dir.x != 0.0f && g.NavWindow->ScrollbarX) { From cf308f403987918f7324047b58bed79eeb57607f Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 29 Sep 2017 00:20:51 +0200 Subject: [PATCH 141/319] Nav: Fixed SetItemDefaultFocus from stealing default focus when we are initializing default focus for a menu bar layer (#787) --- imgui.cpp | 2 +- imgui_demo.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 186f262c..cd6aed41 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4132,7 +4132,7 @@ void ImGui::SetItemAllowOverlap() void ImGui::SetItemDefaultFocus() { ImGuiContext& g = *GImGui; - if (g.NavWindow == g.CurrentWindow->RootNavWindow && (g.NavInitDefaultRequest || g.NavInitDefaultResultId != 0)) + if (g.NavWindow == g.CurrentWindow->RootNavWindow && (g.NavInitDefaultRequest || g.NavInitDefaultResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent) { g.NavInitDefaultRequest = false; g.NavInitDefaultResultExplicit = true; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b7e77a79..637adb8e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2420,7 +2420,7 @@ struct ExampleAppConsole // Demonstrate keeping focus on the input box ImGui::SetItemDefaultFocus(); - if (ImGui::IsItemHovered() || reclaim_focus) + if (reclaim_focus) //|| ImGui::IsItemHovered()) ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget ImGui::End(); From 9737efb2f17759f1633d0bb5fcda622ae0f0603b Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 29 Sep 2017 17:58:25 +0200 Subject: [PATCH 142/319] Nav: Store per-window last nav id also per-layer so we can easily query them for menu navigation code. (#787) --- imgui.cpp | 35 ++++++++++++++++++----------------- imgui_internal.h | 3 ++- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 233b3951..762cc178 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1839,7 +1839,7 @@ ImGuiWindow::ImGuiWindow(const char* name) Appearing = false; BeginCount = 0; PopupId = 0; - NavLastId = 0; + NavLastIds[0] = NavLastIds[1] = 0; AutoFitFramesX = AutoFitFramesY = -1; AutoFitOnlyGrows = false; AutoFitChildAxises = 0x00; @@ -1934,8 +1934,8 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) g.NavId = id; if (window) g.NavLayer = window->DC.NavLayerCurrent; - if (window && window->DC.NavLayerCurrent == 0) // (Assume that id correspond to the current NavLayer, which should be the case) - window->NavLastId = id; + if (window) // NB: We current assume that SetActiveId() is called in the context where its NavLayer is the current one, which should be the case. + window->NavLastIds[window->DC.NavLayerCurrent] = id; } } @@ -2450,9 +2450,9 @@ static void SetNavId(ImGuiID id) { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindow); + IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); g.NavId = id; - if (g.NavLayer == 0) - g.NavWindow->NavLastId = g.NavId; + g.NavWindow->NavLastIds[g.NavLayer] = g.NavId; } static void SetNavIdAndMoveMouse(ImGuiID id, const ImRect& rect_rel) @@ -2470,7 +2470,7 @@ static void NavInitWindow(ImGuiWindow* window, bool force_reinit) { ImGuiContext& g = *GImGui; IM_ASSERT(window == g.NavWindow); - if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastId == 0) || force_reinit) + if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) { SetNavId(0); g.NavInitDefaultRequest = true; @@ -2480,7 +2480,7 @@ static void NavInitWindow(ImGuiWindow* window, bool force_reinit) } else { - g.NavId = window->NavLastId; + g.NavId = window->NavLastIds[0]; } } @@ -2699,7 +2699,7 @@ static void NavUpdate() ImGui::FocusWindow(g.NavWindowingTarget); g.NavDisableHighlight = false; g.NavDisableMouseHover = true; - if (g.NavWindowingTarget->NavLastId == 0) + if (g.NavWindowingTarget->NavLastIds[0] == 0) NavInitWindow(g.NavWindowingTarget, false); } @@ -2711,14 +2711,15 @@ static void NavUpdate() g.NavLayer = (g.NavWindow->DC.NavLayerActiveMask & (1<<1)) ? (g.NavLayer ^ 1) : 0; g.NavDisableHighlight = false; g.NavDisableMouseHover = true; - if (g.NavLayer == 0 && g.NavWindow->NavLastId) - SetNavIdAndMoveMouse(g.NavWindow->NavLastId, ImRect()); + if (g.NavLayer == 0 && g.NavWindow->NavLastIds[0] != 0) + SetNavIdAndMoveMouse(g.NavWindow->NavLastIds[0], ImRect()); else NavInitWindow(g.NavWindow, true); } g.NavWindowingTarget = NULL; } } + IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); // Set output flags for user application g.IO.NavUsable = g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); @@ -2752,8 +2753,8 @@ static void NavUpdate() { // Leave the "menu" layer g.NavLayer = 0; - if (g.NavWindow->NavLastId) - SetNavIdAndMoveMouse(g.NavWindow->NavLastId, ImRect()); + if (g.NavWindow->NavLastIds[0]) + SetNavIdAndMoveMouse(g.NavWindow->NavLastIds[0], ImRect()); else NavInitWindow(g.NavWindow, true); } @@ -2761,7 +2762,7 @@ static void NavUpdate() { // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) - g.NavWindow->NavLastId = 0; + g.NavWindow->NavLastIds[0] = 0; g.NavId = 0; } } @@ -4807,7 +4808,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames == 1); if (window_just_appearing_after_hidden_for_resize) - window->NavLastId = 0; + window->NavLastIds[0] = 0; window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize); // Process SetNextWindow***() calls @@ -5500,7 +5501,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) if (g.NavWindow != window) { - g.NavId = window ? window->NavLastId : 0; // Restore NavId + g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId g.NavIdIsAlive = false; g.NavLayer = 0; if (window && g.NavDisableMouseHover) @@ -9588,7 +9589,7 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImVec2 popu if ((pressed || g.NavActivateId == id) && !popup_open) { if (window->DC.NavLayerCurrent == 0) - window->NavLastId = id; + window->NavLastIds[0] = id; OpenPopupEx(id, false); popup_open = true; } @@ -11617,7 +11618,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y); ImGui::BulletText("Active: %d, Accessed: %d", window->Active, window->Accessed); - ImGui::BulletText("NavLastId: 0x%08x, NavLayerActiveMask: %02X", window->NavLastId, window->DC.NavLayerActiveMask); + ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); diff --git a/imgui_internal.h b/imgui_internal.h index e8d90a9b..f87bd686 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -764,7 +764,6 @@ struct IMGUI_API ImGuiWindow bool Appearing; // Set during the frame where the window is appearing (or re-appearing) int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) - ImGuiID NavLastId; // Last known NavId for this window, for nav layer 0 only. int AutoFitFramesX, AutoFitFramesY; bool AutoFitOnlyGrows; int AutoFitChildAxises; @@ -776,6 +775,8 @@ struct IMGUI_API ImGuiWindow ImVec2 SetWindowPosVal; // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size) ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0,0) when positioning from top-left corner; ImVec2(0.5f,0.5f) for centering; ImVec2(1,1) for bottom right. + ImGuiID NavLastIds[2]; // Last known NavId for this window, per layer (0/1) + ImGuiDrawContext DC; // Temporary per-window data, reset at the beginning of the frame ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack ImRect ClipRect; // = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2. From e3c89aeb10387c17e20a62afaf4d47096087931d Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 29 Sep 2017 18:24:43 +0200 Subject: [PATCH 143/319] Nav: Fixed menuitems/selectable on menu layer (layer 1) from storing themselves in wrong layer of LastNavIds when hovered.causing inconsistencies and bugs when hovering menu with mouse then pressing ALT to return to layer 0). NB: this is not a new bug introduced by the previous commit (which is related to nav layers), bug has been there for a while. (#787) --- imgui.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 762cc178..62d52fb0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2446,19 +2446,19 @@ int ImGui::GetFrameCount() return GImGui->FrameCount; } -static void SetNavId(ImGuiID id) +static void SetNavId(ImGuiID id, int nav_layer) { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindow); - IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); + IM_ASSERT(nav_layer == 0 || nav_layer == 1); g.NavId = id; - g.NavWindow->NavLastIds[g.NavLayer] = g.NavId; + g.NavWindow->NavLastIds[nav_layer] = g.NavId; } -static void SetNavIdAndMoveMouse(ImGuiID id, const ImRect& rect_rel) +static void SetNavIdAndMoveMouse(ImGuiID id, int nav_layer, const ImRect& rect_rel) { ImGuiContext& g = *GImGui; - SetNavId(id); + SetNavId(id, nav_layer); g.NavRefRectRel = rect_rel; g.NavMousePosDirty = true; g.NavDisableHighlight = false; @@ -2472,7 +2472,7 @@ static void NavInitWindow(ImGuiWindow* window, bool force_reinit) IM_ASSERT(window == g.NavWindow); if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) { - SetNavId(0); + SetNavId(0, g.NavLayer); g.NavInitDefaultRequest = true; g.NavInitDefaultResultId = 0; g.NavInitDefaultResultExplicit = false; @@ -2579,7 +2579,7 @@ static void NavUpdate() { // Apply result from previous navigation init request (typically select the first item, unless SetItemDefaultFocus() has been called) IM_ASSERT(g.NavWindow); - SetNavId(g.NavInitDefaultResultId); + SetNavId(g.NavInitDefaultResultId, g.NavLayer); g.NavRefRectRel = g.NavInitDefaultResultRectRel; if (g.NavDisableMouseHover) g.NavMousePosDirty = true; @@ -2627,7 +2627,7 @@ static void NavUpdate() // Apply result from previous frame navigation directional move request ImGui::ClearActiveID(); - SetNavIdAndMoveMouse(g.NavMoveResultId, g.NavMoveResultRectRel); + SetNavIdAndMoveMouse(g.NavMoveResultId, g.NavLayer, g.NavMoveResultRectRel); g.NavMoveFromClampedRefRect = false; } @@ -2712,7 +2712,7 @@ static void NavUpdate() g.NavDisableHighlight = false; g.NavDisableMouseHover = true; if (g.NavLayer == 0 && g.NavWindow->NavLastIds[0] != 0) - SetNavIdAndMoveMouse(g.NavWindow->NavLastIds[0], ImRect()); + SetNavIdAndMoveMouse(g.NavWindow->NavLastIds[0], g.NavLayer, ImRect()); else NavInitWindow(g.NavWindow, true); } @@ -2739,7 +2739,7 @@ static void NavUpdate() ImGuiWindow* parent_window = g.NavWindow->ParentWindow; ImGui::FocusWindow(parent_window); IM_ASSERT(child_window->ChildId != 0); - SetNavId(child_window->ChildId); + SetNavId(child_window->ChildId, g.NavLayer); // FIXME-NAV: Layer not necessarily correct g.NavIdIsAlive = false; if (g.NavDisableMouseHover) g.NavMousePosDirty = true; @@ -2754,7 +2754,7 @@ static void NavUpdate() // Leave the "menu" layer g.NavLayer = 0; if (g.NavWindow->NavLastIds[0]) - SetNavIdAndMoveMouse(g.NavWindow->NavLastIds[0], ImRect()); + SetNavIdAndMoveMouse(g.NavWindow->NavLastIds[0], g.NavLayer, ImRect()); else NavInitWindow(g.NavWindow, true); } @@ -9737,7 +9737,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (hovered && !g.NavDisableMouseHover && g.NavWindow == window && (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)) { g.NavDisableHighlight = true; - SetNavId(id); + SetNavId(id, window->DC.NavLayerCurrent); } // Render From 54eb4c485e898e215669a552dbce10f303bd63c8 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 29 Sep 2017 19:15:24 +0200 Subject: [PATCH 144/319] Nav: Marked misleading/broken code that will never execute. --- imgui.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 62d52fb0..e3d2b652 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3430,11 +3430,14 @@ void ImGui::EndFrame() if (g.HoveredRootWindow != NULL) { FocusWindow(g.HoveredWindow); + // FIXME-NAV: This never execute because of the FocusWindow call above, however we may might this behavior? + /* if (g.NavWindow != g.HoveredWindow) { g.NavRefRectRel = ImRect(g.IO.MousePos - g.HoveredWindow->Pos, g.IO.MousePos - g.HoveredWindow->Pos); //ImRect(0,0,0,0); g.NavDisableHighlight = true; } + */ if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove)) { g.MovedWindow = g.HoveredWindow; From 9712a81f85bc22f9c10f7f9492613dc353b4ebd3 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 29 Sep 2017 19:26:03 +0200 Subject: [PATCH 145/319] Nav: Rectangle rectangle stored per window and per layer as well. Makes things simpler, allows enable us to visualize more data. --- imgui.cpp | 31 ++++++++++++++++++------------- imgui_internal.h | 7 ++++--- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e3d2b652..6ca7697b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1839,7 +1839,6 @@ ImGuiWindow::ImGuiWindow(const char* name) Appearing = false; BeginCount = 0; PopupId = 0; - NavLastIds[0] = NavLastIds[1] = 0; AutoFitFramesX = AutoFitFramesY = -1; AutoFitOnlyGrows = false; AutoFitChildAxises = 0x00; @@ -1848,6 +1847,9 @@ ImGuiWindow::ImGuiWindow(const char* name) SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); + NavLastIds[0] = NavLastIds[1] = 0; + NavRefRectRel[0] = NavRefRectRel[1] = ImRect(); + LastFrameActive = -1; ItemWidthDefault = 0.0f; FontWindowScale = 1.0f; @@ -2223,7 +2225,7 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar // Update window-relative bounding box of navigated item if (g.NavId == *id) { - g.NavRefRectRel = nav_bb_rel; + window->NavRefRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; g.NavIdIsAlive = true; g.NavIdTabCounter = window->FocusIdxTabCounter; } @@ -2459,7 +2461,7 @@ static void SetNavIdAndMoveMouse(ImGuiID id, int nav_layer, const ImRect& rect_r { ImGuiContext& g = *GImGui; SetNavId(id, nav_layer); - g.NavRefRectRel = rect_rel; + g.NavWindow->NavRefRectRel[nav_layer] = rect_rel; g.NavMousePosDirty = true; g.NavDisableHighlight = false; g.NavDisableMouseHover = true; @@ -2487,11 +2489,13 @@ static void NavInitWindow(ImGuiWindow* window, bool force_reinit) static ImVec2 NavCalcPreferredMousePos() { ImGuiContext& g = *GImGui; - if (!g.NavWindow) + ImGuiWindow* window = g.NavWindow; + if (!window) return g.IO.MousePos; - ImVec2 p = g.NavWindow->Pos + ImVec2(g.NavRefRectRel.Min.x + ImMin(g.Style.FramePadding.x*4, g.NavRefRectRel.GetWidth()), g.NavRefRectRel.Max.y - ImMin(g.Style.FramePadding.y, g.NavRefRectRel.GetHeight())); - ImRect r = GetVisibleRect(); - return ImFloor(ImClamp(p, r.Min, r.Max)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. + const ImRect& ref_rect_rel = window->NavRefRectRel[g.NavLayer]; + ImVec2 pos = g.NavWindow->Pos + ImVec2(ref_rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, ref_rect_rel.GetWidth()), ref_rect_rel.Max.y - ImMin(g.Style.FramePadding.y, ref_rect_rel.GetHeight())); + ImRect visible_rect = GetVisibleRect(); + return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. } static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N) @@ -2580,7 +2584,7 @@ static void NavUpdate() // Apply result from previous navigation init request (typically select the first item, unless SetItemDefaultFocus() has been called) IM_ASSERT(g.NavWindow); SetNavId(g.NavInitDefaultResultId, g.NavLayer); - g.NavRefRectRel = g.NavInitDefaultResultRectRel; + g.NavWindow->NavRefRectRel[g.NavLayer] = g.NavInitDefaultResultRectRel; if (g.NavDisableMouseHover) g.NavMousePosDirty = true; } @@ -2834,21 +2838,22 @@ static void NavUpdate() { // 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)) + if (!window_rect_rel.Contains(g.NavWindow->NavRefRectRel[g.NavLayer])) { 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 - g.NavRefRectRel.ClipWith(window_rect_rel); + g.NavWindow->NavRefRectRel[g.NavLayer].ClipWith(window_rect_rel); 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(); + g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + g.NavWindow->NavRefRectRel[g.NavLayer].Min, g.NavWindow->Pos + g.NavWindow->NavRefRectRel[g.NavLayer].Max) : ImRect(); g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] + //if (g.NavWindow) for (int layer = 0; layer < 2; layer++) g.OverlayDrawList.AddRect(g.NavWindow->Pos + g.NavWindow->NavRefRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRefRectRel[layer].Max, IM_COL32(255,200,0,255)); // [DEBUG] } void ImGui::NewFrame() @@ -5509,7 +5514,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavLayer = 0; if (window && g.NavDisableMouseHover) g.NavMousePosDirty = true; - g.NavRefRectRel.Min = g.NavRefRectRel.Max = window ? (window->DC.CursorStartPos - window->Pos) : ImVec2(0,0); + window->NavRefRectRel[0].Min = window->NavRefRectRel[1].Max = window ? (window->DC.CursorStartPos - window->Pos) : ImVec2(0,0); g.NavWindow = window; } @@ -11622,6 +11627,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y); ImGui::BulletText("Active: %d, Accessed: %d", window->Active, window->Accessed); ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); + ImGui::BulletText("NavRefRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRefRectRel[0].Min.x, window->NavRefRectRel[0].Min.y, window->NavRefRectRel[0].Max.x, window->NavRefRectRel[0].Max.y); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); @@ -11655,7 +11661,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("ActiveId: 0x%08X/0x%08X, ActiveIdSource: %s", g.ActiveId, g.ActiveIdPreviousFrame, input_source_names[g.ActiveIdSource]); ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); ImGui::Text("NavWindow: '%s', NavId: 0x%08X, NavLayer: %d", g.NavWindow ? g.NavWindow->Name : "NULL", g.NavId, g.NavLayer); - ImGui::Text("NavRefRectRel: (%.1f,%.1f)(%.1f,%.1f)", g.NavRefRectRel.Min.x, g.NavRefRectRel.Min.y, g.NavRefRectRel.Max.x, g.NavRefRectRel.Max.y); ImGui::Text("NavUsable: %d, NavActive: %d", g.IO.NavUsable, g.IO.NavActive); ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); diff --git a/imgui_internal.h b/imgui_internal.h index f87bd686..44c79735 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -459,7 +459,7 @@ struct ImGuiContext ImGuiID NavId; // Nav/focused item for navigation ImGuiID NavActivateId, NavInputId; // ~~ IsKeyPressedMap(ImGuiKey_NavActive) ? NavId : 0, etc. (to make widget code terser) ImGuiID NavTabbedId; // - ImRect NavRefRectRel, NavScoringRectScreen;// Reference rectangle, in window space. Modified rectangle for directional navigation scoring, in screen space. + ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring. ImGuiWindow* NavWindowingTarget; float NavWindowingDisplayAlpha; bool NavWindowingToggleLayer; @@ -571,7 +571,7 @@ struct ImGuiContext NavWindow = NULL; NavId = NavActivateId = NavInputId = NavTabbedId = 0; - NavRefRectRel = NavScoringRectScreen = ImRect(); + NavScoringRectScreen = ImRect(); NavWindowingTarget = NULL; NavWindowingDisplayAlpha = 0.0f; NavWindowingToggleLayer = false; @@ -775,7 +775,8 @@ struct IMGUI_API ImGuiWindow ImVec2 SetWindowPosVal; // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size) ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0,0) when positioning from top-left corner; ImVec2(0.5f,0.5f) for centering; ImVec2(1,1) for bottom right. - ImGuiID NavLastIds[2]; // Last known NavId for this window, per layer (0/1) + ImGuiID NavLastIds[2]; // Last known NavId for this window, per layer (0/1) + ImRect NavRefRectRel[2]; // Reference rectangle, in window space ImGuiDrawContext DC; // Temporary per-window data, reset at the beginning of the frame ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack From 8a5a29f0beff1da7e432cfbe54cc6f78575b5d38 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 29 Sep 2017 19:28:19 +0200 Subject: [PATCH 146/319] Nav: Renamed internal field. --- imgui.cpp | 22 +++++++++++----------- imgui_internal.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6ca7697b..f65264ac 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1848,7 +1848,7 @@ ImGuiWindow::ImGuiWindow(const char* name) SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); NavLastIds[0] = NavLastIds[1] = 0; - NavRefRectRel[0] = NavRefRectRel[1] = ImRect(); + NavRectRel[0] = NavRectRel[1] = ImRect(); LastFrameActive = -1; ItemWidthDefault = 0.0f; @@ -2225,7 +2225,7 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar // Update window-relative bounding box of navigated item if (g.NavId == *id) { - window->NavRefRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; + window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; g.NavIdIsAlive = true; g.NavIdTabCounter = window->FocusIdxTabCounter; } @@ -2461,7 +2461,7 @@ static void SetNavIdAndMoveMouse(ImGuiID id, int nav_layer, const ImRect& rect_r { ImGuiContext& g = *GImGui; SetNavId(id, nav_layer); - g.NavWindow->NavRefRectRel[nav_layer] = rect_rel; + g.NavWindow->NavRectRel[nav_layer] = rect_rel; g.NavMousePosDirty = true; g.NavDisableHighlight = false; g.NavDisableMouseHover = true; @@ -2492,8 +2492,8 @@ static ImVec2 NavCalcPreferredMousePos() ImGuiWindow* window = g.NavWindow; if (!window) return g.IO.MousePos; - const ImRect& ref_rect_rel = window->NavRefRectRel[g.NavLayer]; - ImVec2 pos = g.NavWindow->Pos + ImVec2(ref_rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, ref_rect_rel.GetWidth()), ref_rect_rel.Max.y - ImMin(g.Style.FramePadding.y, ref_rect_rel.GetHeight())); + const ImRect& rect_rel = window->NavRectRel[g.NavLayer]; + ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); ImRect visible_rect = GetVisibleRect(); return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. } @@ -2584,7 +2584,7 @@ static void NavUpdate() // Apply result from previous navigation init request (typically select the first item, unless SetItemDefaultFocus() has been called) IM_ASSERT(g.NavWindow); SetNavId(g.NavInitDefaultResultId, g.NavLayer); - g.NavWindow->NavRefRectRel[g.NavLayer] = g.NavInitDefaultResultRectRel; + g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitDefaultResultRectRel; if (g.NavDisableMouseHover) g.NavMousePosDirty = true; } @@ -2838,18 +2838,18 @@ static void NavUpdate() { // 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.NavWindow->NavRefRectRel[g.NavLayer])) + if (!window_rect_rel.Contains(g.NavWindow->NavRectRel[g.NavLayer])) { 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 - g.NavWindow->NavRefRectRel[g.NavLayer].ClipWith(window_rect_rel); + g.NavWindow->NavRectRel[g.NavLayer].ClipWith(window_rect_rel); 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.NavWindow->NavRefRectRel[g.NavLayer].Min, g.NavWindow->Pos + g.NavWindow->NavRefRectRel[g.NavLayer].Max) : ImRect(); + g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[g.NavLayer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[g.NavLayer].Max) : ImRect(); g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] @@ -5514,7 +5514,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavLayer = 0; if (window && g.NavDisableMouseHover) g.NavMousePosDirty = true; - window->NavRefRectRel[0].Min = window->NavRefRectRel[1].Max = window ? (window->DC.CursorStartPos - window->Pos) : ImVec2(0,0); + window->NavRectRel[0].Min = window->NavRectRel[1].Max = window ? (window->DC.CursorStartPos - window->Pos) : ImVec2(0,0); g.NavWindow = window; } @@ -11627,7 +11627,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y); ImGui::BulletText("Active: %d, Accessed: %d", window->Active, window->Accessed); ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); - ImGui::BulletText("NavRefRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRefRectRel[0].Min.x, window->NavRefRectRel[0].Min.y, window->NavRefRectRel[0].Max.x, window->NavRefRectRel[0].Max.y); + ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); diff --git a/imgui_internal.h b/imgui_internal.h index 44c79735..83691486 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -776,7 +776,7 @@ struct IMGUI_API ImGuiWindow ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0,0) when positioning from top-left corner; ImVec2(0.5f,0.5f) for centering; ImVec2(1,1) for bottom right. ImGuiID NavLastIds[2]; // Last known NavId for this window, per layer (0/1) - ImRect NavRefRectRel[2]; // Reference rectangle, in window space + ImRect NavRectRel[2]; // Reference rectangle, in window space ImGuiDrawContext DC; // Temporary per-window data, reset at the beginning of the frame ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack From bfaa426f7fda56295da6de33b9252db8f4aaa01a Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 29 Sep 2017 19:34:11 +0200 Subject: [PATCH 147/319] Nav: Minor tidying up (adding local variables to be a little more sane). --- imgui.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f65264ac..961fa148 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2808,25 +2808,26 @@ static void NavUpdate() if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) { // *Fallback* manual-scroll with NavUp/NavDown when window has no navigable item - const float scroll_speed = ImFloor(g.NavWindow->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. - if (!g.NavWindow->DC.NavLayerActiveMask && g.NavWindow->DC.NavHasScroll && g.NavMoveRequest) + ImGuiWindow* window = g.NavWindow; + const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. + if (!window->DC.NavLayerActiveMask && window->DC.NavHasScroll && g.NavMoveRequest) { if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) - SetWindowScrollX(g.NavWindow, ImFloor(g.NavWindow->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); + SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) - SetWindowScrollY(g.NavWindow, ImFloor(g.NavWindow->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); + SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); } // *Normal* Manual scroll with NavScrollXXX keys ImVec2 scroll_dir = GetNavInputAmount2d(1, ImGuiNavReadMode_Down, 1.0f/10.0f, 10.0f); - if (scroll_dir.x != 0.0f && g.NavWindow->ScrollbarX) + if (scroll_dir.x != 0.0f && window->ScrollbarX) { - SetWindowScrollX(g.NavWindow, ImFloor(g.NavWindow->Scroll.x + scroll_dir.x * scroll_speed)); + SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); g.NavMoveFromClampedRefRect = true; } if (scroll_dir.y != 0.0f) { - SetWindowScrollY(g.NavWindow, ImFloor(g.NavWindow->Scroll.y + scroll_dir.y * scroll_speed)); + SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); g.NavMoveFromClampedRefRect = true; } } @@ -2837,12 +2838,13 @@ static void NavUpdate() 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.NavWindow->NavRectRel[g.NavLayer])) + ImGuiWindow* window = g.NavWindow; + ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1,1), window->InnerRect.Max - window->Pos + ImVec2(1,1)); + if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) { - 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 - g.NavWindow->NavRectRel[g.NavLayer].ClipWith(window_rect_rel); + float pad = window->CalcFontSize() * 0.5f; + window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item + window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel); g.NavId = 0; } g.NavMoveFromClampedRefRect = false; @@ -2853,7 +2855,7 @@ static void NavUpdate() g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] - //if (g.NavWindow) for (int layer = 0; layer < 2; layer++) g.OverlayDrawList.AddRect(g.NavWindow->Pos + g.NavWindow->NavRefRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRefRectRel[layer].Max, IM_COL32(255,200,0,255)); // [DEBUG] + //if (g.NavWindow) for (int layer = 0; layer < 2; layer++) g.OverlayDrawList.AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); // [DEBUG] } void ImGui::NewFrame() From 5a9ebeca9e45e10d3e14dad49c33af27eea7ca70 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 29 Sep 2017 19:49:20 +0200 Subject: [PATCH 148/319] Nav: Fix typo from 9712a81f85bc22f9c10f7f9492613dc353b4ebd3 --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 961fa148..a09bb8c6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5516,7 +5516,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavLayer = 0; if (window && g.NavDisableMouseHover) g.NavMousePosDirty = true; - window->NavRectRel[0].Min = window->NavRectRel[1].Max = window ? (window->DC.CursorStartPos - window->Pos) : ImVec2(0,0); + window->NavRectRel[0].Min = window->NavRectRel[0].Max = window ? (window->DC.CursorStartPos - window->Pos) : ImVec2(0,0); g.NavWindow = window; } From cf3b21179b251ce651dcfd9d287cc3049c896c84 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 29 Sep 2017 20:03:02 +0200 Subject: [PATCH 149/319] Nav: Comments --- imgui.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a09bb8c6..eb7e1ea0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2035,7 +2035,6 @@ static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1) } // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057 -// FIXME-NAVIGATION: Pretty rough. Also may want to handle the degenerate case that we have commented out. static bool NavScoreItem(ImRect cand) { ImGuiContext& g = *GImGui; @@ -2043,7 +2042,7 @@ static bool NavScoreItem(ImRect cand) if (g.NavLayer != window->DC.NavLayerCurrent) return false; - const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having lots of items with varied width) + const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) // We perform scoring on items bounding box clipped by their parent window on the other axis (clipping on our movement axis would give us equal scores for all clipped items) if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) @@ -2058,7 +2057,7 @@ static bool NavScoreItem(ImRect cand) } // Compute distance between boxes - // FIXME-NAVIGATION: Introducing various biases toward typical imgui uses cases, but we don't have any rigorous proof of their side-effect.. + // FIXME-NAVIGATION: Introducing biases for vertical navigation, needs to be removed. float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items if (dby && dbx) @@ -2127,7 +2126,7 @@ static bool NavScoreItem(ImRect cand) } else if (dist_center == g.NavMoveResultDistCenter) { - // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" buttons + // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis. if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance @@ -2167,7 +2166,7 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id) // Declare item bounding box for clipping and interaction. // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface -// declares their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd(). +// declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd(). bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_arg) { ImGuiContext& g = *GImGui; From f4e4c3870559781bb64c3817fe2f78ddf8979468 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 29 Sep 2017 22:19:53 +0200 Subject: [PATCH 150/319] BeginMenu(): fixed logic to distinguish vertical menu from one layed out in a menu bar. Makes MenuItem() in a regular window behave more consistently, and this will be also needed by upcoming menu-navigation changes in the nav branch. (#126, #787) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index eb7e1ea0..08e697c4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10029,7 +10029,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) g.NavWindow = backed_nav_window; bool want_open = false, want_close = false; - if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) + if (window->DC.LayoutType != ImGuiLayoutType_Horizontal) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) { // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. bool moving_within_opened_triangle = false; From 1eaa9d0621ef6db263290785c698c27df3301243 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 29 Sep 2017 22:56:51 +0200 Subject: [PATCH 151/319] Nav: Fixed uninitialized variable (that had no side-effects due to the code/data flow involved) --- imgui_internal.h | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui_internal.h b/imgui_internal.h index 83691486..bf1ff1f8 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -583,6 +583,7 @@ struct ImGuiContext NavInitDefaultRequest = false; NavInitDefaultResultId = 0; NavInitDefaultResultExplicit = false; + NavMoveFromClampedRefRect = false; NavMoveRequest = false; NavMoveDir = NavMoveDirLast = ImGuiDir_None; NavMoveResultId = 0; From 587e637db0be03fb11754a59c59383912cbdaf73 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 29 Sep 2017 22:57:44 +0200 Subject: [PATCH 152/319] Nav: Taking note that we should aim to remove MenuBarAppending later. --- imgui_internal.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index bf1ff1f8..5b399b52 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -665,8 +665,9 @@ struct IMGUI_API ImGuiDrawContext bool LastItemRectHoveredRect; bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) int NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) - int NavLayerActiveMask, NavLayerActiveMaskNext; // Which layer have been written to. - bool MenuBarAppending; + int NavLayerActiveMask; // Which layer have been written to (result from previous frame) + int NavLayerActiveMaskNext; // Which layer have been written to (buffer for current frame) + bool MenuBarAppending; // FIXME: Remove this float MenuBarOffsetX; ImVector ChildWindows; ImGuiStorage* StateStorage; From 88a354585a87b69ad27b1570736a22860f78ca13 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 29 Sep 2017 23:36:41 +0200 Subject: [PATCH 153/319] Nav: It's now possible to navigate sibling of a menu-bar while navigating inside one of their child. If a Left<>Right navigation request fails to find a match we forward the request to the root menu. (#787, #126) Currently the sibling menu is isn't automatically opened, that's still left to it (and even that can be anoying in Windows when the first menu-item is a child menu) --- imgui.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++------ imgui_internal.h | 11 ++++++---- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 08e697c4..deeb7c34 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2634,6 +2634,15 @@ static void NavUpdate() g.NavMoveFromClampedRefRect = false; } + // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame + if (g.NavMoveRequestForwardStep == 2) + { + IM_ASSERT(g.NavMoveRequest); + if (g.NavMoveResultId == 0) + g.NavDisableHighlight = false; + g.NavMoveRequestForwardStep = 0; + } + // Apply application mouse position movement, after we had a chance to process move request result. if (g.NavMousePosDirty && g.NavIdIsAlive) { @@ -2781,14 +2790,24 @@ static void NavUpdate() // Initiate directional inputs request const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; - g.NavMoveDir = ImGuiDir_None; - if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) + if (g.NavMoveRequestForwardStep == 0) { - if ((allowed_dir_flags & (1<Flags & ImGuiWindowFlags_NoNavInputs)) + { + if ((allowed_dir_flags & (1<InnerRect.Min - window->Pos - ImVec2(1,1), window->InnerRect.Max - window->Pos + ImVec2(1,1)); if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) @@ -5231,6 +5251,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; window->DC.ChildWindows.resize(0); window->DC.LayoutType = ImGuiLayoutType_Vertical; + window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; window->DC.ItemFlags = ImGuiItemFlags_Default_; window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled @@ -9966,6 +9987,27 @@ void ImGui::EndMenuBar() ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; + ImGuiContext& g = *GImGui; + + // When a move request within one of our child menu failed, capture the request to navigate among our siblings. + if (g.NavMoveRequest && g.NavMoveResultId == 0 && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)) + { + ImGuiWindow* nav_earliest_child = g.NavWindow; + while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) + nav_earliest_child = nav_earliest_child->ParentWindow; + if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForwardStep == 0) + { + // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. + // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost) + IM_ASSERT(window->DC.NavLayerActiveMaskNext & 0x02); // Sanity check + FocusWindow(window); + SetNavIdAndMoveMouse(window->NavLastIds[1], 1, window->NavRectRel[1]); + g.NavLayer = 1; + g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. + g.NavMoveRequest = false; + g.NavMoveRequestForwardStep = 1; + } + } IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); IM_ASSERT(window->DC.MenuBarAppending); diff --git a/imgui_internal.h b/imgui_internal.h index 5b399b52..bf0876c3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -473,8 +473,9 @@ struct ImGuiContext ImGuiID NavInitDefaultResultId; 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 + bool NavMoveRequest; // Move request for this frame + int NavMoveRequestForwardStep; // 0: no forward, 1: forward request, 2: forward result ImGuiDir NavMoveDir; // West/East/North/South ImGuiDir NavMoveDirLast; // ImGuiID NavMoveResultId; // Best move request candidate @@ -585,6 +586,7 @@ struct ImGuiContext NavInitDefaultResultExplicit = false; NavMoveFromClampedRefRect = false; NavMoveRequest = false; + NavMoveRequestForwardStep = 0; NavMoveDir = NavMoveDirLast = ImGuiDir_None; NavMoveResultId = 0; NavMoveResultDistBox = NavMoveResultDistCenter = NavMoveResultDistAxial = 0.0f; @@ -663,8 +665,8 @@ struct IMGUI_API ImGuiDrawContext ImGuiID LastItemId; ImRect LastItemRect; bool LastItemRectHoveredRect; - bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) - int NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) + bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) + int NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) int NavLayerActiveMask; // Which layer have been written to (result from previous frame) int NavLayerActiveMaskNext; // Which layer have been written to (buffer for current frame) bool MenuBarAppending; // FIXME: Remove this @@ -672,6 +674,7 @@ struct IMGUI_API ImGuiDrawContext ImVector ChildWindows; ImGuiStorage* StateStorage; ImGuiLayoutType LayoutType; + ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. ImGuiItemFlags ItemFlags; // == ItemFlagsStack.back() [empty == ImGuiItemFlags_Default] @@ -714,7 +717,7 @@ struct IMGUI_API ImGuiDrawContext MenuBarAppending = false; MenuBarOffsetX = 0.0f; StateStorage = NULL; - LayoutType = ImGuiLayoutType_Vertical; + LayoutType = ParentLayoutType = ImGuiLayoutType_Vertical; ItemWidth = 0.0f; ItemFlags = ImGuiItemFlags_Default_; TextWrapPos = -1.0f; From ecd72cc0c7e85733f68ecec2fb0b472f28524123 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 29 Sep 2017 22:09:55 +0200 Subject: [PATCH 154/319] Nav: Disabled the final axial check when considering candidates in most situations except menubars. It's definitively undesirable inside Menu as we want to catch nav request failures reliably. I think it may be considered as an option if we find this desirable i some circumstances. Right now ideally I'd remove it totally but with current scoring setup, without it we can't easily reach the Window Close button. (#787) --- imgui.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index deeb7c34..9031b9c6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2098,7 +2098,7 @@ static bool NavScoreItem(ImRect cand) if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) { char buf[128]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "db (%.0f,%.0f->%.5f) dc (%.0f,%.0f->%.5f) da (%.0f,%.0f->%.5f) quad %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[quadrant]); + ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f) dcen (%.2f,%.2f->%.4f) d (%.2f,%.2f->%.4f) nav %c, quad %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); g.OverlayDrawList.AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); g.OverlayDrawList.AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150)); g.OverlayDrawList.AddText(cand.Max, ~0U, buf); @@ -2138,8 +2138,10 @@ static bool NavScoreItem(ImRect cand) // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness) // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too. - if (g.NavMoveResultDistBox == FLT_MAX) - if (dist_axial < g.NavMoveResultDistAxial) // Check axial match + // 2017/09/29: FIXME: This now currently only enabled inside menubars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward. + // Disabling it may however lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? + if (g.NavMoveResultDistBox == FLT_MAX && dist_axial < g.NavMoveResultDistAxial) // Check axial match + if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f)) g.NavMoveResultDistAxial = dist_axial, new_best = true; @@ -9990,7 +9992,7 @@ void ImGui::EndMenuBar() ImGuiContext& g = *GImGui; // When a move request within one of our child menu failed, capture the request to navigate among our siblings. - if (g.NavMoveRequest && g.NavMoveResultId == 0 && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)) + if (g.NavMoveRequest && g.NavMoveResultId == 0 && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) { ImGuiWindow* nav_earliest_child = g.NavWindow; while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) From 0046c618b06db9a794ac2fdca920a55f92969a25 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 30 Sep 2017 00:47:07 +0200 Subject: [PATCH 155/319] Nav: Allow to collapse tree nodes with NavLeft and open them with NavRight (#787, #1079) --- imgui.cpp | 29 +++++++++++++++++++++++------ imgui_demo.cpp | 2 +- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9031b9c6..894bab7a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7044,14 +7044,31 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowOverlapMode) ? ImGuiButtonFlags_AllowOverlapMode : 0); if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0); + bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); - if (pressed && !(flags & ImGuiTreeNodeFlags_Leaf)) + if (!(flags & ImGuiTreeNodeFlags_Leaf)) { - bool toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) || (g.NavActivateId == id); - if (flags & ImGuiTreeNodeFlags_OpenOnArrow) - toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)) && (!g.NavDisableMouseHover); - if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) - toggled |= g.IO.MouseDoubleClicked[0]; + bool toggled = false; + if (pressed) + { + toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) || (g.NavActivateId == id); + if (flags & ImGuiTreeNodeFlags_OpenOnArrow) + toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)) && (!g.NavDisableMouseHover); + if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) + toggled |= g.IO.MouseDoubleClicked[0]; + } + + if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open) + { + toggled = true; + g.NavMoveRequest = false; + } + if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? + { + toggled = true; + g.NavMoveRequest = false; + } + if (toggled) { is_open = !is_open; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 637adb8e..879cd9b8 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -456,9 +456,9 @@ void ImGui::ShowTestWindow(bool* p_open) if (ImGui::TreeNode("Collapsing Headers")) { static bool closable_group = true; + ImGui::Checkbox("Enable extra group", &closable_group); if (ImGui::CollapsingHeader("Header")) { - ImGui::Checkbox("Enable extra group", &closable_group); for (int i = 0; i < 5; i++) ImGui::Text("Some content %d", i); } From 491edfd8d8ff7c17143b26cd25a4d63ebb1b5a25 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 30 Sep 2017 23:46:23 +0200 Subject: [PATCH 156/319] Nav: Fixed a crash introduced yesterday. --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 894bab7a..68f1e76c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5538,7 +5538,8 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavLayer = 0; if (window && g.NavDisableMouseHover) g.NavMousePosDirty = true; - window->NavRectRel[0].Min = window->NavRectRel[0].Max = window ? (window->DC.CursorStartPos - window->Pos) : ImVec2(0,0); + if (window) + window->NavRectRel[0].Min = window->NavRectRel[0].Max = window->DC.CursorStartPos - window->Pos; g.NavWindow = window; } From 74da533c939cdc367b3af98a7a6594b67fd51451 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 3 Oct 2017 11:22:38 -0700 Subject: [PATCH 157/319] Nav: Added NavJustNavigatedId internal info to record when we land on a given item after a navigation request, useful for various algorithms (currently looking at range selection stuff) (#787) --- imgui.cpp | 8 +++++--- imgui_internal.h | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 68f1e76c..711b9ab5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1928,7 +1928,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) if (id) { g.ActiveIdIsAlive = true; - g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavTabbedId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; + g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustNavigatedId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; if (g.ActiveIdSource == ImGuiInputSource_Nav) g.NavDisableMouseHover = true; else @@ -2318,7 +2318,7 @@ bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop return true; if (allow_keyboard_focus && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent) { - g.NavTabbedId = id; + g.NavJustTabbedId = id; return true; } @@ -2592,6 +2592,7 @@ static void NavUpdate() g.NavInitDefaultRequest = false; g.NavInitDefaultResultExplicit = false; g.NavInitDefaultResultId = 0; + g.NavJustNavigatedId = 0; // Process navigation move request if (g.NavMoveRequest && g.NavMoveResultId != 0) @@ -2633,6 +2634,7 @@ static void NavUpdate() // Apply result from previous frame navigation directional move request ImGui::ClearActiveID(); SetNavIdAndMoveMouse(g.NavMoveResultId, g.NavLayer, g.NavMoveResultRectRel); + g.NavJustNavigatedId = g.NavMoveResultId; g.NavMoveFromClampedRefRect = false; } @@ -2657,7 +2659,7 @@ static void NavUpdate() g.NavMousePosDirty = false; } g.NavIdIsAlive = false; - g.NavTabbedId = 0; + g.NavJustTabbedId = 0; // Navigation windowing mode (change focus, move/resize window) if (!g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiNavReadMode_Pressed)) diff --git a/imgui_internal.h b/imgui_internal.h index bf0876c3..8c3a1764 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -458,7 +458,8 @@ struct ImGuiContext ImGuiWindow* NavWindow; // Nav/focused window for navigation ImGuiID NavId; // Nav/focused item for navigation ImGuiID NavActivateId, NavInputId; // ~~ IsKeyPressedMap(ImGuiKey_NavActive) ? NavId : 0, etc. (to make widget code terser) - ImGuiID NavTabbedId; // + ImGuiID NavJustTabbedId; // Just tabbed to this id. + ImGuiID NavJustNavigatedId; // Just navigated to this id (result of a successfully MoveRequest) ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring. ImGuiWindow* NavWindowingTarget; float NavWindowingDisplayAlpha; @@ -571,7 +572,7 @@ struct ImGuiContext SettingsDirtyTimer = 0.0f; NavWindow = NULL; - NavId = NavActivateId = NavInputId = NavTabbedId = 0; + NavId = NavActivateId = NavInputId = NavJustTabbedId = 0; NavScoringRectScreen = ImRect(); NavWindowingTarget = NULL; NavWindowingDisplayAlpha = 0.0f; From 64a51327d3d87b98644311600cea202df6da9809 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 3 Oct 2017 13:25:40 -0700 Subject: [PATCH 158/319] Nav: Added NavMoveResultParentId internal info to record when the parent/context of a given NavId. Useful for various algorithms (currently looking at range selection stuff) (#787) --- imgui.cpp | 2 ++ imgui_internal.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 711b9ab5..112ce24e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2220,6 +2220,7 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar if (new_best) { g.NavMoveResultId = *id; + g.NavMoveResultParentId = window->IDStack.back(); g.NavMoveResultRectRel = nav_bb_rel; } @@ -2856,6 +2857,7 @@ static void NavUpdate() // Reset search g.NavMoveResultId = 0; + g.NavMoveResultParentId = 0; g.NavMoveResultDistAxial = g.NavMoveResultDistBox = g.NavMoveResultDistCenter = FLT_MAX; // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we clamp its bounding box (used for search) to the visible area to restart navigation within visible items diff --git a/imgui_internal.h b/imgui_internal.h index 8c3a1764..3798e7fa 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -480,6 +480,7 @@ struct ImGuiContext ImGuiDir NavMoveDir; // West/East/North/South ImGuiDir NavMoveDirLast; // ImGuiID NavMoveResultId; // Best move request candidate + ImGuiID NavMoveResultParentId; // float NavMoveResultDistBox; // Best move request candidate box distance to current NavId float NavMoveResultDistCenter; // Best move request candidate center distance to current NavId float NavMoveResultDistAxial; @@ -590,6 +591,7 @@ struct ImGuiContext NavMoveRequestForwardStep = 0; NavMoveDir = NavMoveDirLast = ImGuiDir_None; NavMoveResultId = 0; + NavMoveResultParentId = 0; NavMoveResultDistBox = NavMoveResultDistCenter = NavMoveResultDistAxial = 0.0f; SetNextWindowPosVal = ImVec2(0.0f, 0.0f); From cb4e6c8212d736e1ac0ac5a5911239af23660fc8 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 3 Oct 2017 13:44:48 -0700 Subject: [PATCH 159/319] Nav: Selectable(): activating selectable also sets NavId + removed the MouseDelta test (added in 43ee5d73e9f524320489720639942ff60bbfaf54, #323) as I don't think it is needed. (#787) --- imgui.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 112ce24e..954f6424 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9788,11 +9788,12 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl selected = false; // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets) - if (hovered && !g.NavDisableMouseHover && g.NavWindow == window && (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)) - { - g.NavDisableHighlight = true; - SetNavId(id, window->DC.NavLayerCurrent); - } + if (pressed || hovered)// && (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)) + if (!g.NavDisableMouseHover && g.NavWindow == window) + { + g.NavDisableHighlight = true; + SetNavId(id, window->DC.NavLayerCurrent); + } // Render if (hovered || selected) From 6ea90af6b76f2b9f7b86c12813bafd6f0408bdc0 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 3 Oct 2017 13:53:17 -0700 Subject: [PATCH 160/319] Nav: Highlight clipped within host window then extruded out. (#787) --- imgui.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 954f6424..805844f2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2155,14 +2155,16 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id) return; ImGuiWindow* window = ImGui::GetCurrentWindow(); + ImRect display_rect = bb; + display_rect.ClipWith(window->ClipRect); const float THICKNESS = 2.0f; const float DISTANCE = 3.0f + THICKNESS * 0.5f; - ImRect display_rect(bb.Min - ImVec2(DISTANCE,DISTANCE), bb.Max + ImVec2(DISTANCE,DISTANCE)); - if (!window->ClipRect.Contains(display_rect)) + display_rect.Expand(ImVec2(DISTANCE,DISTANCE)); + bool fully_visible = window->ClipRect.Contains(display_rect); + if (!fully_visible) window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), g.Style.FrameRounding, 0x0F, THICKNESS); - //window->DrawList->AddRect(g.NavRefRectScreen.Min, g.NavRefRectScreen.Max, IM_COL32(255,0,0,255)); - if (!window->ClipRect.Contains(display_rect)) + if (!fully_visible) window->DrawList->PopClipRect(); } From a56b71e866ff658be06b7bb2688d75d637372581 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 3 Oct 2017 13:54:19 -0700 Subject: [PATCH 161/319] Nav: Added code to render thin highlight type. (#787) --- imgui.cpp | 31 ++++++++++++++++++++----------- imgui_internal.h | 10 +++++++++- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 805844f2..45d90f70 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2148,24 +2148,33 @@ static bool NavScoreItem(ImRect cand) return new_best; } -void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id) +void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags) { ImGuiContext& g = *GImGui; - if (id != g.NavId || g.NavDisableHighlight) + if (id != g.NavId) return; + if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysRender)) + return; ImGuiWindow* window = ImGui::GetCurrentWindow(); ImRect display_rect = bb; display_rect.ClipWith(window->ClipRect); - const float THICKNESS = 2.0f; - const float DISTANCE = 3.0f + THICKNESS * 0.5f; - display_rect.Expand(ImVec2(DISTANCE,DISTANCE)); - bool fully_visible = window->ClipRect.Contains(display_rect); - if (!fully_visible) - window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); - window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), g.Style.FrameRounding, 0x0F, THICKNESS); - if (!fully_visible) - window->DrawList->PopClipRect(); + if (flags & ImGuiNavHighlightFlags_TypeDefault) + { + const float THICKNESS = 2.0f; + const float DISTANCE = 3.0f + THICKNESS * 0.5f; + display_rect.Expand(ImVec2(DISTANCE,DISTANCE)); + bool fully_visible = window->ClipRect.Contains(display_rect); + if (!fully_visible) + window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); + window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), g.Style.FrameRounding, 0x0F, THICKNESS); + if (!fully_visible) + window->DrawList->PopClipRect(); + } + if (flags & ImGuiNavHighlightFlags_TypeThin) + { + window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), g.Style.FrameRounding, ~0, 1.0f); + } } // Declare item bounding box for clipping and interaction. diff --git a/imgui_internal.h b/imgui_internal.h index 3798e7fa..9ef0fbb1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -51,6 +51,7 @@ typedef int ImGuiTreeNodeFlags; // enum ImGuiTreeNodeFlags_ typedef int ImGuiSliderFlags; // enum ImGuiSliderFlags_ typedef int ImGuiSeparatorFlags; // enum ImGuiSeparatorFlags_ typedef int ImGuiItemFlags; // enum ImGuiItemFlags_ +typedef int ImGuiNavHighlightFlags; //------------------------------------------------------------------------- // STB libraries @@ -250,6 +251,13 @@ enum ImGuiDir ImGuiDir_Down = 3 }; +enum ImGuiNavHighlightFlags_ +{ + ImGuiNavHighlightFlags_TypeDefault = 1 << 0, + ImGuiNavHighlightFlags_TypeThin = 1 << 1, + ImGuiNavHighlightFlags_AlwaysRender = 1 << 2 +}; + enum ImGuiCorner { ImGuiCorner_TopLeft = 1 << 0, // 1 @@ -895,7 +903,7 @@ namespace ImGui IMGUI_API void RenderCollapseTriangle(ImVec2 pos, bool is_open, float scale = 1.0f); IMGUI_API void RenderBullet(ImVec2 pos); IMGUI_API void RenderCheckMark(ImVec2 pos, ImU32 col); - IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id); // Navigation highlight + IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_TypeDefault); // Navigation highlight IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text. From 42ee537be38ede09fefc9d1b02d85c62359d93ae Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 4 Oct 2017 10:45:35 -0700 Subject: [PATCH 162/319] Comments --- imgui_internal.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 9ef0fbb1..d170f458 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -202,8 +202,8 @@ enum ImGuiColumnsFlags_ enum ImGuiSelectableFlagsPrivate_ { // NB: need to be in sync with last value of ImGuiSelectableFlags_ - ImGuiSelectableFlags_Menu = 1 << 3, - ImGuiSelectableFlags_MenuItem = 1 << 4, + ImGuiSelectableFlags_Menu = 1 << 3, // -> PressedOnClick + ImGuiSelectableFlags_MenuItem = 1 << 4, // -> PressedOnRelease ImGuiSelectableFlags_Disabled = 1 << 5, ImGuiSelectableFlags_DrawFillAvailWidth = 1 << 6 }; From f326fac64a7d25898fbd3fc7c2f7ac152bd18a3d Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 5 Oct 2017 10:29:17 -0700 Subject: [PATCH 163/319] Nav: Menus and popups now have move request wrap around vertically (using the move request forward thing added recently). (#787) --- imgui.cpp | 19 ++++++++++++++++++- imgui_internal.h | 6 +++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6502e787..b2711c4c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4476,11 +4476,28 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags ext return is_open; } +static void NavProcessMoveRequestWrapAround(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (g.NavMoveRequest && g.NavWindow == window && g.NavMoveResultId == 0) + if ((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) && g.NavMoveRequestForwardStep == 0 && g.NavLayer == 0) + { + g.NavMoveRequest = false; + g.NavMoveRequestForwardStep = 1; + g.NavWindow->NavRectRel[0].Min.y = g.NavWindow->NavRectRel[0].Max.y = (g.NavMoveDir == ImGuiDir_Up) ? window->SizeFull.y : 0.0f; + } +} + void ImGui::EndPopup() { + ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls - IM_ASSERT(GImGui->CurrentPopupStack.Size > 0); + IM_ASSERT(g.CurrentPopupStack.Size > 0); + + // Make all menus and popups wrap around for now, may need to expose that policy. + NavProcessMoveRequestWrapAround(window); + End(); if (!(window->Flags & ImGuiWindowFlags_Modal)) PopStyleVar(); diff --git a/imgui_internal.h b/imgui_internal.h index d170f458..1bb47505 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -484,9 +484,9 @@ struct ImGuiContext bool NavInitDefaultResultExplicit; // Whether the result was explicitly requested with SetItemDefaultFocus() bool NavMoveFromClampedRefRect; // Set by manual scrolling, if we scroll to a point where NavId isn't visible we reset navigation from visible items bool NavMoveRequest; // Move request for this frame - int NavMoveRequestForwardStep; // 0: no forward, 1: forward request, 2: forward result - ImGuiDir NavMoveDir; // West/East/North/South - ImGuiDir NavMoveDirLast; // + int NavMoveRequestForwardStep; // 0: no forward, 1: forward request, 2: forward result (this is used to navigate sibling parent menus from a child menu) + ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down) + ImGuiDir NavMoveDirLast; // Direction of the previous move request ImGuiID NavMoveResultId; // Best move request candidate ImGuiID NavMoveResultParentId; // float NavMoveResultDistBox; // Best move request candidate box distance to current NavId From f2c9bd8d4f527892502c7032edda347a142bb703 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 5 Oct 2017 16:39:59 -0700 Subject: [PATCH 164/319] Nav: Fixed uninitialized context variables for sanity. --- imgui_internal.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/imgui_internal.h b/imgui_internal.h index 1bb47505..10efd74c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -573,6 +573,7 @@ struct ImGuiContext ActiveIdIsAlive = false; ActiveIdIsJustActivated = false; ActiveIdAllowOverlap = false; + ActiveIdAllowNavDirFlags = 0; ActiveIdClickOffset = ImVec2(-1,-1); ActiveIdWindow = NULL; ActiveIdSource = ImGuiInputSource_None; @@ -586,6 +587,7 @@ struct ImGuiContext NavWindowingTarget = NULL; NavWindowingDisplayAlpha = 0.0f; NavWindowingToggleLayer = false; + NavLayer = 0; NavIdTabCounter = INT_MAX; NavIdIsAlive = false; NavMousePosDirty = false; From 518f02f4de99cefd78566d78edb59073f7b0b8d5 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 5 Oct 2017 18:10:59 -0700 Subject: [PATCH 165/319] Demo: better demo for SetKeyboardFocusHere() --- imgui_demo.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 879cd9b8..22adf891 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1753,6 +1753,16 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf)); if (ImGui::IsItemActive()) has_focus = 3; ImGui::PopAllowKeyboardFocus(); + + // Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item + static float f3[3] = { 0.0f, 0.0f, 0.0f }; + int focus_ahead = -1; + if (ImGui::Button("Focus on X")) focus_ahead = 0; ImGui::SameLine(); + if (ImGui::Button("Focus on Y")) focus_ahead = 1; ImGui::SameLine(); + if (ImGui::Button("Focus on Z")) focus_ahead = 2; + if (focus_ahead != -1) ImGui::SetKeyboardFocusHere(focus_ahead); + ImGui::SliderFloat3("Float3", &f3[0], 0.0f, 1.0f); + if (has_focus) ImGui::Text("Item with focus: %d", has_focus); else From 2f27b733be7f42835dc67d85cae6024b3559c97b Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 5 Oct 2017 18:33:40 -0700 Subject: [PATCH 166/319] Nav: Fixed uninitialized context variables for sanity. --- imgui_internal.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui_internal.h b/imgui_internal.h index 10efd74c..e8873737 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -582,7 +582,8 @@ struct ImGuiContext SettingsDirtyTimer = 0.0f; NavWindow = NULL; - NavId = NavActivateId = NavInputId = NavJustTabbedId = 0; + NavId = NavActivateId = NavInputId = 0; + NavJustTabbedId = NavJustNavigatedId = 0; NavScoringRectScreen = ImRect(); NavWindowingTarget = NULL; NavWindowingDisplayAlpha = 0.0f; From 80c4e2fe7b8fe5aa194ef3e29137b6f893fa1ff4 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 5 Oct 2017 20:35:47 -0700 Subject: [PATCH 167/319] Nav: Tidying up activation mechanism so that setting NavActivateId can trigger buttons. --- imgui.cpp | 44 +++++++++++++++++++++++++++----------------- imgui_internal.h | 5 +++-- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0016d57c..6a466f5f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2818,13 +2818,23 @@ static void NavUpdate() } } - g.NavActivateId = (g.NavId && !g.NavDisableHighlight && !g.NavWindowingTarget && g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadActivate, ImGuiNavReadMode_Pressed)) ? g.NavId : 0; - g.NavInputId = (g.NavId && !g.NavDisableHighlight && !g.NavWindowingTarget && g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadInput, ImGuiNavReadMode_Pressed)) ? g.NavId : 0; + g.NavActivateId = g.NavActivateDownId = g.NavInputId = 0; + if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget) + { + if (g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadActivate, ImGuiNavReadMode_Pressed)) + g.NavActivateId = g.NavId; + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputDown(ImGuiNavInput_PadActivate)) + g.NavActivateDownId = g.NavId; + if (g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadInput, ImGuiNavReadMode_Pressed)) + g.NavInputId = g.NavId; + } if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { - g.NavActivateId = g.NavInputId = 0; + g.NavActivateId = g.NavActivateDownId = g.NavInputId = 0; g.NavDisableHighlight = true; } + if (g.NavActivateId != 0) + IM_ASSERT(g.NavActivateDownId == g.NavActivateId); g.NavMoveRequest = false; // Initiate directional inputs request @@ -6681,18 +6691,18 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool { // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse hovered = true; - if (!g.NavWindowingTarget && IsNavInputDown(ImGuiNavInput_PadActivate)) + } + if (g.NavActivateDownId == id) + { + bool nav_pressed = (g.NavActivateId == id) || IsNavInputPressed(ImGuiNavInput_PadActivate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiNavReadMode_Repeat : ImGuiNavReadMode_Pressed); + if (nav_pressed) + pressed = true; + if (nav_pressed || g.ActiveId == id) { - bool nav_pressed = IsNavInputPressed(ImGuiNavInput_PadActivate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiNavReadMode_Repeat : ImGuiNavReadMode_Pressed); - if (nav_pressed) - pressed = true; - if (nav_pressed || g.ActiveId == id) - { - // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. - g.NavActivateId = id; // This is so SetActiveId assign a Nav source - SetActiveID(id, window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - } + // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. + g.NavActivateId = id; // This is so SetActiveId assign a Nav source + SetActiveID(id, window); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); } } @@ -6717,7 +6727,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool } else if (g.ActiveIdSource == ImGuiInputSource_Nav) { - if (!IsNavInputDown(ImGuiNavInput_PadActivate)) + if (g.NavActivateDownId != id) ClearActiveID(); } } @@ -7677,7 +7687,7 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v clicked_t = 1.0f - clicked_t; set_new_value = true; } - else if (g.ActiveIdSource == ImGuiInputSource_Nav && IsNavInputDown(ImGuiNavInput_PadActivate)) + else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivateDownId == id) { const ImVec2 delta2 = GetNavInputAmount2d(0, ImGuiNavReadMode_RepeatFast, 0.0f, 0.0f); if (float delta = is_horizontal ? delta2.x : -delta2.y) @@ -7997,7 +8007,7 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s // Process clicking on the drag if (g.ActiveId == id) { - if (g.IO.MouseDown[0] || IsNavInputDown(ImGuiNavInput_PadActivate)) + if (g.IO.MouseDown[0] || g.NavActivateDownId == id) { if (g.ActiveIdIsJustActivated) { diff --git a/imgui_internal.h b/imgui_internal.h index e8873737..318520d8 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -465,7 +465,8 @@ struct ImGuiContext // Navigation data (for gamepad/keyboard) ImGuiWindow* NavWindow; // Nav/focused window for navigation ImGuiID NavId; // Nav/focused item for navigation - ImGuiID NavActivateId, NavInputId; // ~~ IsKeyPressedMap(ImGuiKey_NavActive) ? NavId : 0, etc. (to make widget code terser) + ImGuiID NavActivateId, NavActivateDownId; // ~~ IsNavInputPressed(ImGuiNavInput_PadActivate) ? NavId : 0, etc. + ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_PadInput) ? NavId : 0, etc. ImGuiID NavJustTabbedId; // Just tabbed to this id. ImGuiID NavJustNavigatedId; // Just navigated to this id (result of a successfully MoveRequest) ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring. @@ -582,7 +583,7 @@ struct ImGuiContext SettingsDirtyTimer = 0.0f; NavWindow = NULL; - NavId = NavActivateId = NavInputId = 0; + NavId = NavActivateId = NavActivateDownId = NavInputId = 0; NavJustTabbedId = NavJustNavigatedId = 0; NavScoringRectScreen = ImRect(); NavWindowingTarget = NULL; From 59c6f35bf6b8919000d7deacc0a7b1c1428a2519 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 5 Oct 2017 21:25:20 -0700 Subject: [PATCH 168/319] Added ActivateItem(), GetItemID() functions. --- imgui.cpp | 17 +++++++++++++++++ imgui.h | 6 +++++- imgui_demo.cpp | 27 ++++++++++++++++++++++----- imgui_internal.h | 3 ++- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6a466f5f..7c76018d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2837,6 +2837,11 @@ static void NavUpdate() IM_ASSERT(g.NavActivateDownId == g.NavActivateId); g.NavMoveRequest = false; + // Process explicit activation request + if (g.NavNextActivateId != 0) + g.NavActivateId = g.NavActivateDownId = g.NavInputId = g.NavNextActivateId; + g.NavNextActivateId = 0; + // Initiate directional inputs request const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; if (g.NavMoveRequestForwardStep == 0) @@ -6379,6 +6384,18 @@ void ImGui::SetScrollHere(float center_y_ratio) SetScrollFromPosY(target_y, center_y_ratio); } +void ImGui::ActivateItem(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + g.NavNextActivateId = id; +} + +ImGuiID ImGui::GetItemID() +{ + ImGuiContext& g = *GImGui; + return g.CurrentWindow->DC.LastItemId; +} + void ImGui::SetKeyboardFocusHere(int offset) { IM_ASSERT(offset >= -1); // -1 is allowed but not below diff --git a/imgui.h b/imgui.h index 8e47249e..c3601751 100644 --- a/imgui.h +++ b/imgui.h @@ -177,7 +177,6 @@ namespace ImGui IMGUI_API void SetScrollY(float scroll_y); // set scrolling amount [0..GetScrollMaxY()] IMGUI_API void SetScrollHere(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. IMGUI_API void SetScrollFromPosY(float pos_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position valid. use GetCursorPos() or GetCursorStartPos()+offset to get valid positions. - IMGUI_API void SetKeyboardFocusHere(int offset = 0); // FIXME-NAVIGATION // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget. IMGUI_API void SetStateStorage(ImGuiStorage* tree); // replace tree state storage with our own (if you want to manipulate it yourself, typically clear subsection of it) IMGUI_API ImGuiStorage* GetStateStorage(); @@ -416,6 +415,11 @@ namespace ImGui // Styles IMGUI_API void StyleColorsClassic(ImGuiStyle* dst = NULL); + // Focus, Activation + IMGUI_API void ActivateItem(ImGuiID id); // remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. + IMGUI_API ImGuiID GetItemID(); // get id of previous item, generally ~GetID(label) + IMGUI_API void SetKeyboardFocusHere(int offset = 0); // FIXME-NAVIGATION // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget. + // Utilities IMGUI_API bool IsItemHovered(); // is the last item hovered by mouse (and usable)? or we are currently using Nav and the item is focused. IMGUI_API bool IsItemRectHovered(); // is the last item hovered by mouse? even if another item is active or window is blocked by popup while we are hovering this diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 22adf891..c058fa1e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1754,6 +1754,11 @@ void ImGui::ShowTestWindow(bool* p_open) if (ImGui::IsItemActive()) has_focus = 3; ImGui::PopAllowKeyboardFocus(); + if (has_focus) + ImGui::Text("Item with focus: %d", has_focus); + else + ImGui::Text("Item with focus: "); + // Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item static float f3[3] = { 0.0f, 0.0f, 0.0f }; int focus_ahead = -1; @@ -1763,14 +1768,26 @@ void ImGui::ShowTestWindow(bool* p_open) if (focus_ahead != -1) ImGui::SetKeyboardFocusHere(focus_ahead); ImGui::SliderFloat3("Float3", &f3[0], 0.0f, 1.0f); - if (has_focus) - ImGui::Text("Item with focus: %d", has_focus); - else - ImGui::Text("Item with focus: "); - ImGui::TextWrapped("Cursor & selection are preserved when refocusing last used item in code."); + ImGui::TextWrapped("NB: Cursor & selection are preserved when refocusing last used item in code."); ImGui::TreePop(); } +#if 0 + if (ImGui::TreeNode("Remote Activation")) + { + static char label[256]; + ImGui::InputText("Label", label, IM_ARRAYSIZE(label)); + ImGui::PopID(); // We don't yet have an easy way compute ID at other levels of the ID stack so we pop it manually for now (e.g. we'd like something like GetID("../label")) + ImGuiID id = ImGui::GetID(label); + ImGui::PushID("Remote Activation"); + if (ImGui::SmallButton("Activate")) + ImGui::ActivateItem(id); + ImGui::SameLine(); + ImGui::Text("ID = 0x%08X", id); + ImGui::TreePop(); + } +#endif + if (ImGui::TreeNode("Dragging")) { ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget."); diff --git a/imgui_internal.h b/imgui_internal.h index 318520d8..e9660a2f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -469,6 +469,7 @@ struct ImGuiContext ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_PadInput) ? NavId : 0, etc. ImGuiID NavJustTabbedId; // Just tabbed to this id. ImGuiID NavJustNavigatedId; // Just navigated to this id (result of a successfully MoveRequest) + ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring. ImGuiWindow* NavWindowingTarget; float NavWindowingDisplayAlpha; @@ -584,7 +585,7 @@ struct ImGuiContext NavWindow = NULL; NavId = NavActivateId = NavActivateDownId = NavInputId = 0; - NavJustTabbedId = NavJustNavigatedId = 0; + NavJustTabbedId = NavJustNavigatedId = NavNextActivateId = 0; NavScoringRectScreen = ImRect(); NavWindowingTarget = NULL; NavWindowingDisplayAlpha = 0.0f; From f5bd4663ddc4228da8cf94abfda5d8614bdb922e Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 5 Oct 2017 22:02:13 -0700 Subject: [PATCH 169/319] Nav: Moving the big chunk of code from ItemAdd() to NavProcessItem() --- imgui.cpp | 89 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7c76018d..96e506b0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2200,6 +2200,50 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl } } +static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) +{ + ImGuiContext& g = *GImGui; + const ImGuiItemFlags item_flags = window->DC.ItemFlags; + const ImRect nav_bb_rel(nav_bb.Min - g.NavWindow->Pos, nav_bb.Max - g.NavWindow->Pos); + if (g.NavInitDefaultRequest && g.NavLayer == window->DC.NavLayerCurrent) + { + // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback + if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) + g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = false; // Found a match, clear request + if (g.NavInitDefaultResultId == 0 || !(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) + { + g.NavInitDefaultResultId = id; + g.NavInitDefaultResultRectRel = nav_bb_rel; + } + } + + bool new_best = false; +#if IMGUI_DEBUG_NAV + // [DEBUG] Score items at all times + if (!g.NavMoveRequest) + g.NavMoveDir = g.NavMoveDirLast; + if (g.NavId != id) + new_best = NavScoreItem(nav_bb) && g.NavMoveRequest; +#else + if (g.NavMoveRequest && g.NavId != id) + new_best = NavScoreItem(nav_bb); +#endif + if (new_best) + { + g.NavMoveResultId = id; + g.NavMoveResultParentId = window->IDStack.back(); + g.NavMoveResultRectRel = nav_bb_rel; + } + + // Update window-relative bounding box of navigated item + if (g.NavId == id) + { + window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; + g.NavIdIsAlive = true; + g.NavIdTabCounter = window->FocusIdxTabCounter; + } +} + // Declare item bounding box for clipping and interaction. // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface // declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd(). @@ -2221,51 +2265,10 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar // We could early out with `if (is_clipped && !g.NavInitDefaultRequest) return false;` but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) // A more pragmatic solution for handling long lists is relying on the fact that they are likely evenly spread items (so that clipper can be used) and we could Nav at higher-level (apply index, etc.) // So eventually we would like to provide the user will the primitives to be able to implement that customized/efficient navigation handling whenever necessary. - const ImGuiItemFlags item_flags = window->DC.ItemFlags; if (id != NULL && g.NavWindow == window->RootNavWindow) if (g.NavId == *id || g.NavMoveRequest || g.NavInitDefaultRequest || IMGUI_DEBUG_NAV) - if (g.IO.NavUsable && !(item_flags & ImGuiItemFlags_NoNav)) - { - const ImRect& nav_bb = nav_bb_arg ? *nav_bb_arg : bb; - const ImRect nav_bb_rel(nav_bb.Min - g.NavWindow->Pos, nav_bb.Max - g.NavWindow->Pos); - if (g.NavInitDefaultRequest && g.NavLayer == window->DC.NavLayerCurrent) - { - // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback - if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) - g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = false; // Found a match, clear request - if (g.NavInitDefaultResultId == 0 || !(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) - { - g.NavInitDefaultResultId = *id; - g.NavInitDefaultResultRectRel = nav_bb_rel; - } - } - - bool new_best = false; -#if IMGUI_DEBUG_NAV - // [DEBUG] Score items at all times - if (!g.NavMoveRequest) - g.NavMoveDir = g.NavMoveDirLast; - if (g.NavId != *id) - new_best = NavScoreItem(nav_bb) && g.NavMoveRequest; -#else - if (g.NavMoveRequest && g.NavId != *id) - new_best = NavScoreItem(nav_bb); -#endif - if (new_best) - { - g.NavMoveResultId = *id; - g.NavMoveResultParentId = window->IDStack.back(); - g.NavMoveResultRectRel = nav_bb_rel; - } - - // Update window-relative bounding box of navigated item - if (g.NavId == *id) - { - window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; - g.NavIdIsAlive = true; - g.NavIdTabCounter = window->FocusIdxTabCounter; - } - } + if (g.IO.NavUsable) + NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, *id); if (is_clipped) return false; From a77dd02e8aa404687e97dd9b0c01509d868ba3dc Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 5 Oct 2017 22:06:46 -0700 Subject: [PATCH 170/319] Nav: Moving code next to its peers --- imgui.cpp | 58 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 96e506b0..d593e0ff 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2171,35 +2171,6 @@ static bool NavScoreItem(ImRect cand) return new_best; } -void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags) -{ - ImGuiContext& g = *GImGui; - if (id != g.NavId) - return; - if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysRender)) - return; - ImGuiWindow* window = ImGui::GetCurrentWindow(); - - ImRect display_rect = bb; - display_rect.ClipWith(window->ClipRect); - if (flags & ImGuiNavHighlightFlags_TypeDefault) - { - const float THICKNESS = 2.0f; - const float DISTANCE = 3.0f + THICKNESS * 0.5f; - display_rect.Expand(ImVec2(DISTANCE,DISTANCE)); - bool fully_visible = window->ClipRect.Contains(display_rect); - if (!fully_visible) - window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); - window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), g.Style.FrameRounding, 0x0F, THICKNESS); - if (!fully_visible) - window->DrawList->PopClipRect(); - } - if (flags & ImGuiNavHighlightFlags_TypeThin) - { - window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), g.Style.FrameRounding, ~0, 1.0f); - } -} - static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) { ImGuiContext& g = *GImGui; @@ -3870,6 +3841,35 @@ void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col) window->DrawList->PathStroke(col, false); } +void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags) +{ + ImGuiContext& g = *GImGui; + if (id != g.NavId) + return; + if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysRender)) + return; + ImGuiWindow* window = ImGui::GetCurrentWindow(); + + ImRect display_rect = bb; + display_rect.ClipWith(window->ClipRect); + if (flags & ImGuiNavHighlightFlags_TypeDefault) + { + const float THICKNESS = 2.0f; + const float DISTANCE = 3.0f + THICKNESS * 0.5f; + display_rect.Expand(ImVec2(DISTANCE,DISTANCE)); + bool fully_visible = window->ClipRect.Contains(display_rect); + if (!fully_visible) + window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); + window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), g.Style.FrameRounding, 0x0F, THICKNESS); + if (!fully_visible) + window->DrawList->PopClipRect(); + } + if (flags & ImGuiNavHighlightFlags_TypeThin) + { + window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), g.Style.FrameRounding, ~0, 1.0f); + } +} + // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker. // CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize) ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width) From 40df7a51559e24c0c97494353d0232326741d997 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 6 Oct 2017 09:40:13 -0700 Subject: [PATCH 171/319] Nav: Removed possibly redundant test prior to calling NavProcessItem() from ItemAdd() --- imgui.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d593e0ff..15007e50 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2174,6 +2174,8 @@ static bool NavScoreItem(ImRect cand) static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) { ImGuiContext& g = *GImGui; + //if (!g.IO.NavUsable) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag. + // return; const ImGuiItemFlags item_flags = window->DC.ItemFlags; const ImRect nav_bb_rel(nav_bb.Min - g.NavWindow->Pos, nav_bb.Max - g.NavWindow->Pos); if (g.NavInitDefaultRequest && g.NavLayer == window->DC.NavLayerCurrent) @@ -2238,8 +2240,7 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar // So eventually we would like to provide the user will the primitives to be able to implement that customized/efficient navigation handling whenever necessary. if (id != NULL && g.NavWindow == window->RootNavWindow) if (g.NavId == *id || g.NavMoveRequest || g.NavInitDefaultRequest || IMGUI_DEBUG_NAV) - if (g.IO.NavUsable) - NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, *id); + NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, *id); if (is_clipped) return false; @@ -2482,7 +2483,11 @@ static void NavInitWindow(ImGuiWindow* window, bool force_reinit) { ImGuiContext& g = *GImGui; IM_ASSERT(window == g.NavWindow); - if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) + bool init_for_nav = false; + if (!(window->Flags & ImGuiWindowFlags_NoNavInputs)) + if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) + init_for_nav = true; + if (init_for_nav) { SetNavId(0, g.NavLayer); g.NavInitDefaultRequest = true; From e12cfa9dfd8ae80f70f8229f6510e225bee1fe68 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 6 Oct 2017 09:44:50 -0700 Subject: [PATCH 172/319] Nav: Minor midying up and comments, --- imgui.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 15007e50..bfba93ea 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2228,19 +2228,19 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar window->DC.LastItemId = id ? *id : 0; window->DC.LastItemRect = bb; window->DC.LastItemRectHoveredRect = false; - if (id != NULL) - window->DC.NavLayerActiveMaskNext |= (1 << window->DC.NavLayerCurrent); // Navigation processing runs prior to clipping early-out // (a) So that NavInitDefaultRequest can be honored, for newly opened windows to select a default widget // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests unfortunately, but it is still limited to one window. - // it may not scale very well for windows with ten of thousands of item, but at least the NavRequest is only performed on user interaction, aka maximum once a frame. + // it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. // We could early out with `if (is_clipped && !g.NavInitDefaultRequest) return false;` but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) - // A more pragmatic solution for handling long lists is relying on the fact that they are likely evenly spread items (so that clipper can be used) and we could Nav at higher-level (apply index, etc.) - // So eventually we would like to provide the user will the primitives to be able to implement that customized/efficient navigation handling whenever necessary. - if (id != NULL && g.NavWindow == window->RootNavWindow) - if (g.NavId == *id || g.NavMoveRequest || g.NavInitDefaultRequest || IMGUI_DEBUG_NAV) - NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, *id); + if (id != NULL) + { + window->DC.NavLayerActiveMaskNext |= (1 << window->DC.NavLayerCurrent); + if (g.NavWindow == window->RootNavWindow) + if (g.NavId == *id || g.NavMoveRequest || g.NavInitDefaultRequest || IMGUI_DEBUG_NAV) + NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, *id); + } if (is_clipped) return false; From f451785c9efea3f26997aae0169d6db9189f166b Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 6 Oct 2017 10:17:01 -0700 Subject: [PATCH 173/319] Nav: Shuffled code in ItemAdd() so that NavProcessItem() may have access to LastItemId (will be required by tabbing) --- imgui.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bfba93ea..56102bbc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2113,7 +2113,7 @@ static bool NavScoreItem(ImRect cand) } else { - // Degenerate case: two overlapping buttons with same center, break ties using order + // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter) quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; } @@ -2190,6 +2190,7 @@ static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGu } } + // Scoring for navigation bool new_best = false; #if IMGUI_DEBUG_NAV // [DEBUG] Score items at all times @@ -2224,10 +2225,6 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - const bool is_clipped = IsClippedEx(bb, id, false); - window->DC.LastItemId = id ? *id : 0; - window->DC.LastItemRect = bb; - window->DC.LastItemRectHoveredRect = false; // Navigation processing runs prior to clipping early-out // (a) So that NavInitDefaultRequest can be honored, for newly opened windows to select a default widget @@ -2242,8 +2239,15 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, *id); } + // Clipping test + store basic information about the current item. + const bool is_clipped = IsClippedEx(bb, id, false); + window->DC.LastItemId = id ? *id : 0; + window->DC.LastItemRect = bb; if (is_clipped) + { + window->DC.LastItemRectHoveredRect = false; return false; + } //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) From d16309ca777414c1ea13dbef3d1051bd98a1223d Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 6 Oct 2017 14:40:53 -0700 Subject: [PATCH 174/319] Internal: ItemAdd() minor shallow tweaks --- imgui.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7ecdf92c..c8ff306f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2226,23 +2226,24 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - // Navigation processing runs prior to clipping early-out - // (a) So that NavInitDefaultRequest can be honored, for newly opened windows to select a default widget - // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests unfortunately, but it is still limited to one window. - // it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. - // We could early out with `if (is_clipped && !g.NavInitDefaultRequest) return false;` but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) if (id != 0) { + // Navigation processing runs prior to clipping early-out + // (a) So that NavInitDefaultRequest can be honored, for newly opened windows to select a default widget + // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests unfortunately, but it is still limited to one window. + // it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. + // We could early out with `if (is_clipped && !g.NavInitDefaultRequest) return false;` but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) window->DC.NavLayerActiveMaskNext |= (1 << window->DC.NavLayerCurrent); if (g.NavWindow == window->RootNavWindow) if (g.NavId == id || g.NavMoveRequest || g.NavInitDefaultRequest || IMGUI_DEBUG_NAV) NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id); } - // Clipping test + store basic information about the current item. - const bool is_clipped = IsClippedEx(bb, id, false); window->DC.LastItemId = id; window->DC.LastItemRect = bb; + + // Clipping test + const bool is_clipped = IsClippedEx(bb, id, false); if (is_clipped) { window->DC.LastItemRectHoveredRect = false; From c3105919bacacabe207a7064ba2f636a20f136cf Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 6 Oct 2017 14:51:15 -0700 Subject: [PATCH 175/319] Nav: Minor tweaks also toward removing processing from ItemAdd() --- imgui.cpp | 10 +++++++--- imgui_internal.h | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c8ff306f..207993c4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2233,7 +2233,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests unfortunately, but it is still limited to one window. // it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. // We could early out with `if (is_clipped && !g.NavInitDefaultRequest) return false;` but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) - window->DC.NavLayerActiveMaskNext |= (1 << window->DC.NavLayerCurrent); + window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask; if (g.NavWindow == window->RootNavWindow) if (g.NavId == id || g.NavMoveRequest || g.NavInitDefaultRequest || IMGUI_DEBUG_NAV) NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id); @@ -5377,9 +5377,10 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us if (!(flags & ImGuiWindowFlags_NoTitleBar)) { // Close & collapse button are on layer 1 (same as menus) and don't default focus - const ImGuiItemFlags backup_item_options = window->DC.ItemFlags; + const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; window->DC.NavLayerCurrent++; + window->DC.NavLayerCurrentMask <<= 1; // Collapse button if (!(flags & ImGuiWindowFlags_NoCollapse)) @@ -5403,7 +5404,8 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us } window->DC.NavLayerCurrent--; - window->DC.ItemFlags = backup_item_options; + window->DC.NavLayerCurrentMask >>= 1; + window->DC.ItemFlags = item_flags_backup; // Title text (FIXME: refactor text alignment facilities along with RenderText helpers) const ImVec2 text_size = CalcTextSize(name, NULL, true); @@ -10093,6 +10095,7 @@ bool ImGui::BeginMenuBar() window->DC.CursorPos = ImVec2(rect.Min.x + window->DC.MenuBarOffsetX, rect.Min.y);// + g.Style.FramePadding.y); window->DC.LayoutType = ImGuiLayoutType_Horizontal; window->DC.NavLayerCurrent++; + window->DC.NavLayerCurrentMask <<= 1; window->DC.MenuBarAppending = true; AlignFirstTextHeightToWidgets(); return true; @@ -10134,6 +10137,7 @@ void ImGui::EndMenuBar() EndGroup(); window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.NavLayerCurrent--; + window->DC.NavLayerCurrentMask >>= 1; window->DC.MenuBarAppending = false; } diff --git a/imgui_internal.h b/imgui_internal.h index c31fc4e6..f8519e99 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -683,6 +683,7 @@ struct IMGUI_API ImGuiDrawContext bool LastItemRectHoveredRect; bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) int NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) + int NavLayerCurrentMask; // = (1 << NavLayerCurrent) used by ItemAdd prior to clipping. int NavLayerActiveMask; // Which layer have been written to (result from previous frame) int NavLayerActiveMaskNext; // Which layer have been written to (buffer for current frame) bool MenuBarAppending; // FIXME: Remove this @@ -730,6 +731,7 @@ struct IMGUI_API ImGuiDrawContext NavHasScroll = false; NavLayerActiveMask = NavLayerActiveMaskNext = 0x00; NavLayerCurrent = 0; + NavLayerCurrentMask = 1 << 0; MenuBarAppending = false; MenuBarOffsetX = 0.0f; StateStorage = NULL; From d91b093be64350c73beb645e97abe57a38725678 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 6 Oct 2017 14:56:25 -0700 Subject: [PATCH 176/319] Nav: Minor tweaks in NavProcessItem() used by ItemAdd() --- imgui.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 207993c4..99741746 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2191,22 +2191,22 @@ static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGu } // Scoring for navigation - bool new_best = false; -#if IMGUI_DEBUG_NAV - // [DEBUG] Score items at all times - if (!g.NavMoveRequest) - g.NavMoveDir = g.NavMoveDirLast; if (g.NavId != id) - new_best = NavScoreItem(nav_bb) && g.NavMoveRequest; -#else - if (g.NavMoveRequest && g.NavId != id) - new_best = NavScoreItem(nav_bb); -#endif - if (new_best) { - g.NavMoveResultId = id; - g.NavMoveResultParentId = window->IDStack.back(); - g.NavMoveResultRectRel = nav_bb_rel; +#if IMGUI_DEBUG_NAV + // [DEBUG] Score all items in NavWindow at all times + if (!g.NavMoveRequest) + g.NavMoveDir = g.NavMoveDirLast; + bool new_best = NavScoreItem(nav_bb) && g.NavMoveRequest; +#else + bool new_best = g.NavMoveRequest && NavScoreItem(nav_bb); +#endif + if (new_best) + { + g.NavMoveResultId = id; + g.NavMoveResultParentId = window->IDStack.back(); + g.NavMoveResultRectRel = nav_bb_rel; + } } // Update window-relative bounding box of navigated item From d2975115cd43778994b7226d259896b7ae2f0956 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 6 Oct 2017 15:14:28 -0700 Subject: [PATCH 177/319] Nav: Caching into g.NavAnyRequest to minimize hot path cost (and so we can add many more request sources) --- imgui.cpp | 26 +++++++++++++++++++++++--- imgui_internal.h | 2 ++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 99741746..0289c4a9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2171,6 +2171,19 @@ static bool NavScoreItem(ImRect cand) return new_best; } +static inline void NavUpdateAnyRequestFlag() +{ + ImGuiContext& g = *GImGui; + g.NavAnyRequest = g.NavMoveRequest || g.NavInitDefaultRequest || IMGUI_DEBUG_NAV; +} + +static void NavMoveRequestCancel() +{ + ImGuiContext& g = *GImGui; + g.NavMoveRequest = false; + NavUpdateAnyRequestFlag(); +} + static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) { ImGuiContext& g = *GImGui; @@ -2182,7 +2195,10 @@ static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGu { // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) + { g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = false; // Found a match, clear request + NavUpdateAnyRequestFlag(); + } if (g.NavInitDefaultResultId == 0 || !(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) { g.NavInitDefaultResultId = id; @@ -2235,7 +2251,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) // We could early out with `if (is_clipped && !g.NavInitDefaultRequest) return false;` but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask; if (g.NavWindow == window->RootNavWindow) - if (g.NavId == id || g.NavMoveRequest || g.NavInitDefaultRequest || IMGUI_DEBUG_NAV) + if (g.NavId == id || g.NavAnyRequest) NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id); } @@ -2499,6 +2515,7 @@ static void NavInitWindow(ImGuiWindow* window, bool force_reinit) g.NavInitDefaultResultId = 0; g.NavInitDefaultResultExplicit = false; g.NavInitDefaultResultRectRel = ImRect(); + NavUpdateAnyRequestFlag(); } else { @@ -2860,6 +2877,8 @@ static void NavUpdate() g.NavDisableHighlight = false; } + NavUpdateAnyRequestFlag(); + // Scrolling if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) { @@ -4237,6 +4256,7 @@ void ImGui::SetItemDefaultFocus() g.NavInitDefaultResultExplicit = true; g.NavInitDefaultResultId = g.NavWindow->DC.LastItemId; g.NavInitDefaultResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); + NavUpdateAnyRequestFlag(); if (!IsItemVisible()) SetScrollHere(); } @@ -4511,8 +4531,8 @@ static void NavProcessMoveRequestWrapAround(ImGuiWindow* window) if (g.NavMoveRequest && g.NavWindow == window && g.NavMoveResultId == 0) if ((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) && g.NavMoveRequestForwardStep == 0 && g.NavLayer == 0) { - g.NavMoveRequest = false; g.NavMoveRequestForwardStep = 1; + NavMoveRequestCancel(); g.NavWindow->NavRectRel[0].Min.y = g.NavWindow->NavRectRel[0].Max.y = (g.NavMoveDir == ImGuiDir_Up) ? window->SizeFull.y : 0.0f; } } @@ -10123,8 +10143,8 @@ void ImGui::EndMenuBar() SetNavIdAndMoveMouse(window->NavLastIds[1], 1, window->NavRectRel[1]); g.NavLayer = 1; g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. - g.NavMoveRequest = false; g.NavMoveRequestForwardStep = 1; + NavMoveRequestCancel(); } } diff --git a/imgui_internal.h b/imgui_internal.h index f8519e99..670b07d7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -480,6 +480,7 @@ struct ImGuiContext bool NavMousePosDirty; bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (nb: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. + bool NavAnyRequest; // ~~ NavMoveRequest || NavInitDefaultRequest bool NavInitDefaultRequest; // Init request for appearing window to select first item ImGuiID NavInitDefaultResultId; ImRect NavInitDefaultResultRectRel; @@ -596,6 +597,7 @@ struct ImGuiContext NavMousePosDirty = false; NavDisableHighlight = true; NavDisableMouseHover = false; + NavAnyRequest = false; NavInitDefaultRequest = false; NavInitDefaultResultId = 0; NavInitDefaultResultExplicit = false; From 4d83078885e3568c4b32662a87d61518d0696279 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 6 Oct 2017 15:50:32 -0700 Subject: [PATCH 178/319] Nav: Moving code next to its peers --- imgui.cpp | 30 +++++++++++++++--------------- imgui.h | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0289c4a9..ebc415b6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4247,21 +4247,6 @@ void ImGui::SetItemAllowOverlap() g.ActiveIdAllowOverlap = true; } -void ImGui::SetItemDefaultFocus() -{ - ImGuiContext& g = *GImGui; - if (g.NavWindow == g.CurrentWindow->RootNavWindow && (g.NavInitDefaultRequest || g.NavInitDefaultResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent) - { - g.NavInitDefaultRequest = false; - g.NavInitDefaultResultExplicit = true; - g.NavInitDefaultResultId = g.NavWindow->DC.LastItemId; - g.NavInitDefaultResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); - NavUpdateAnyRequestFlag(); - if (!IsItemVisible()) - SetScrollHere(); - } -} - ImVec2 ImGui::GetItemRectMin() { ImGuiWindow* window = GetCurrentWindowRead(); @@ -6441,6 +6426,21 @@ void ImGui::SetKeyboardFocusHere(int offset) window->FocusIdxTabRequestNext = INT_MAX; } +void ImGui::SetItemDefaultFocus() +{ + ImGuiContext& g = *GImGui; + if (g.NavWindow == g.CurrentWindow->RootNavWindow && (g.NavInitDefaultRequest || g.NavInitDefaultResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent) + { + g.NavInitDefaultRequest = false; + g.NavInitDefaultResultExplicit = true; + g.NavInitDefaultResultId = g.NavWindow->DC.LastItemId; + g.NavInitDefaultResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); + NavUpdateAnyRequestFlag(); + if (!IsItemVisible()) + SetScrollHere(); + } +} + void ImGui::SetStateStorage(ImGuiStorage* tree) { ImGuiWindow* window = GetCurrentWindow(); diff --git a/imgui.h b/imgui.h index b49dacb1..7dda2d3e 100644 --- a/imgui.h +++ b/imgui.h @@ -419,6 +419,7 @@ namespace ImGui IMGUI_API void ActivateItem(ImGuiID id); // remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. IMGUI_API ImGuiID GetItemID(); // get id of previous item, generally ~GetID(label) IMGUI_API void SetKeyboardFocusHere(int offset = 0); // FIXME-NAVIGATION // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget. + IMGUI_API void SetItemDefaultFocus(); // FIXME-NAVIGATION // make last item the default focused item of a window // Utilities IMGUI_API bool IsItemHovered(); // is the last item hovered by mouse (and usable)? or we are currently using Nav and the item is focused. @@ -434,7 +435,6 @@ namespace ImGui IMGUI_API ImVec2 GetItemRectMax(); // " IMGUI_API ImVec2 GetItemRectSize(); // " IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. - IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window IMGUI_API bool IsWindowFocused(); // is current window focused IMGUI_API bool IsWindowHovered(); // is current window hovered and hoverable (not blocked by a popup) (differentiate child windows from each others) IMGUI_API bool IsWindowRectHovered(); // is current window rectangle hovered, disregarding of any consideration of being blocked by a popup. (unlike IsWindowHovered() this will return true even if the window is blocked because of a popup) From 69dd89535818c4413c9c0b9f13c0e23c6277c03a Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 6 Oct 2017 15:54:29 -0700 Subject: [PATCH 179/319] Nav: Renaming NavInitDefaultRequest -> NavInitRequest --- imgui.cpp | 52 ++++++++++++++++++++++++------------------------ imgui_internal.h | 16 +++++++-------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ebc415b6..ced2f747 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2174,7 +2174,7 @@ static bool NavScoreItem(ImRect cand) static inline void NavUpdateAnyRequestFlag() { ImGuiContext& g = *GImGui; - g.NavAnyRequest = g.NavMoveRequest || g.NavInitDefaultRequest || IMGUI_DEBUG_NAV; + g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || IMGUI_DEBUG_NAV; } static void NavMoveRequestCancel() @@ -2191,18 +2191,18 @@ static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGu // return; const ImGuiItemFlags item_flags = window->DC.ItemFlags; const ImRect nav_bb_rel(nav_bb.Min - g.NavWindow->Pos, nav_bb.Max - g.NavWindow->Pos); - if (g.NavInitDefaultRequest && g.NavLayer == window->DC.NavLayerCurrent) + if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) { // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) { - g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = false; // Found a match, clear request + g.NavInitRequest = g.NavInitResultExplicit = false; // Found a match, clear request NavUpdateAnyRequestFlag(); } - if (g.NavInitDefaultResultId == 0 || !(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) + if (g.NavInitResultId == 0 || !(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) { - g.NavInitDefaultResultId = id; - g.NavInitDefaultResultRectRel = nav_bb_rel; + g.NavInitResultId = id; + g.NavInitResultRectRel = nav_bb_rel; } } @@ -2245,10 +2245,10 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) if (id != 0) { // Navigation processing runs prior to clipping early-out - // (a) So that NavInitDefaultRequest can be honored, for newly opened windows to select a default widget + // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests unfortunately, but it is still limited to one window. // it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. - // We could early out with `if (is_clipped && !g.NavInitDefaultRequest) return false;` but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) + // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask; if (g.NavWindow == window->RootNavWindow) if (g.NavId == id || g.NavAnyRequest) @@ -2511,10 +2511,10 @@ static void NavInitWindow(ImGuiWindow* window, bool force_reinit) if (init_for_nav) { SetNavId(0, g.NavLayer); - g.NavInitDefaultRequest = true; - g.NavInitDefaultResultId = 0; - g.NavInitDefaultResultExplicit = false; - g.NavInitDefaultResultRectRel = ImRect(); + g.NavInitRequest = true; + g.NavInitResultId = 0; + g.NavInitResultExplicit = false; + g.NavInitResultRectRel = ImRect(); NavUpdateAnyRequestFlag(); } else @@ -2616,18 +2616,18 @@ static void NavUpdate() g.IO.WantMoveMouse = false; // Process navigation init request (select first/default focus) - if (g.NavInitDefaultResultId != 0 && (!g.NavDisableHighlight || g.NavInitDefaultResultExplicit)) + if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitResultExplicit)) { // Apply result from previous navigation init request (typically select the first item, unless SetItemDefaultFocus() has been called) IM_ASSERT(g.NavWindow); - SetNavId(g.NavInitDefaultResultId, g.NavLayer); - g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitDefaultResultRectRel; + SetNavId(g.NavInitResultId, g.NavLayer); + g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel; if (g.NavDisableMouseHover) g.NavMousePosDirty = true; } - g.NavInitDefaultRequest = false; - g.NavInitDefaultResultExplicit = false; - g.NavInitDefaultResultId = 0; + g.NavInitRequest = false; + g.NavInitResultExplicit = false; + g.NavInitResultId = 0; g.NavJustNavigatedId = 0; // Process navigation move request @@ -2775,7 +2775,7 @@ static void NavUpdate() // Set output flags for user application g.IO.NavUsable = g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); - g.IO.NavActive = (g.IO.NavUsable && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitDefaultRequest; + g.IO.NavActive = (g.IO.NavUsable && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; // Process NavCancel input (to close a popup, get back to parent, clear focus) if (IsNavInputPressed(ImGuiNavInput_PadCancel, ImGuiNavReadMode_Pressed)) @@ -2872,8 +2872,8 @@ static void NavUpdate() // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match if (g.NavMoveRequest && g.NavId == 0) { - g.NavInitDefaultRequest = g.NavInitDefaultResultExplicit = true; - g.NavInitDefaultResultId = 0; + g.NavInitRequest = g.NavInitResultExplicit = true; + g.NavInitResultId = 0; g.NavDisableHighlight = false; } @@ -6429,12 +6429,12 @@ void ImGui::SetKeyboardFocusHere(int offset) void ImGui::SetItemDefaultFocus() { ImGuiContext& g = *GImGui; - if (g.NavWindow == g.CurrentWindow->RootNavWindow && (g.NavInitDefaultRequest || g.NavInitDefaultResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent) + if (g.NavWindow == g.CurrentWindow->RootNavWindow && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent) { - g.NavInitDefaultRequest = false; - g.NavInitDefaultResultExplicit = true; - g.NavInitDefaultResultId = g.NavWindow->DC.LastItemId; - g.NavInitDefaultResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); + g.NavInitRequest = false; + g.NavInitResultExplicit = true; + g.NavInitResultId = g.NavWindow->DC.LastItemId; + g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); NavUpdateAnyRequestFlag(); if (!IsItemVisible()) SetScrollHere(); diff --git a/imgui_internal.h b/imgui_internal.h index 670b07d7..77084dc7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -480,11 +480,11 @@ struct ImGuiContext bool NavMousePosDirty; bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (nb: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. - bool NavAnyRequest; // ~~ NavMoveRequest || NavInitDefaultRequest - bool NavInitDefaultRequest; // Init request for appearing window to select first item - ImGuiID NavInitDefaultResultId; - ImRect NavInitDefaultResultRectRel; - bool NavInitDefaultResultExplicit; // Whether the result was explicitly requested with SetItemDefaultFocus() + bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest + bool NavInitRequest; // Init request for appearing window to select first item + ImGuiID NavInitResultId; + ImRect NavInitResultRectRel; + bool NavInitResultExplicit; // Whether the result was explicitly requested with SetItemDefaultFocus() bool NavMoveFromClampedRefRect; // Set by manual scrolling, if we scroll to a point where NavId isn't visible we reset navigation from visible items bool NavMoveRequest; // Move request for this frame int NavMoveRequestForwardStep; // 0: no forward, 1: forward request, 2: forward result (this is used to navigate sibling parent menus from a child menu) @@ -598,9 +598,9 @@ struct ImGuiContext NavDisableHighlight = true; NavDisableMouseHover = false; NavAnyRequest = false; - NavInitDefaultRequest = false; - NavInitDefaultResultId = 0; - NavInitDefaultResultExplicit = false; + NavInitRequest = false; + NavInitResultId = 0; + NavInitResultExplicit = false; NavMoveFromClampedRefRect = false; NavMoveRequest = false; NavMoveRequestForwardStep = 0; From 7a14d7dfdcc045163e3a301b6dcf40df16dfb765 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 11 Oct 2017 19:05:36 +0200 Subject: [PATCH 180/319] Nav: More consistently drawing a (thin) navigation rectangle hover filled frames such as tree nodes, collapsing header, menus. (#787) --- imgui.cpp | 8 ++++++-- imgui_internal.h | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d431a7ee..39c25233 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3878,7 +3878,7 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl ImGuiContext& g = *GImGui; if (id != g.NavId) return; - if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysRender)) + if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw)) return; ImGuiWindow* window = ImGui::GetCurrentWindow(); @@ -7204,6 +7204,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l { // Framed type RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin); RenderCollapseTriangle(bb.Min + ImVec2(padding.x, text_base_offset_y), is_open, 1.0f); if (g.LogEnabled) { @@ -7223,8 +7224,10 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l { // Unframed typed for tree nodes if (hovered || (flags & ImGuiTreeNodeFlags_Selected)) + { RenderFrame(bb.Min, bb.Max, col, false); - + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin); + } if (flags & ImGuiTreeNodeFlags_Bullet) RenderBullet(bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y)); else if (!(flags & ImGuiTreeNodeFlags_Leaf)) @@ -9914,6 +9917,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl { const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f); + RenderNavHighlight(bb_with_spacing, id, ImGuiNavHighlightFlags_TypeThin); } if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1) diff --git a/imgui_internal.h b/imgui_internal.h index 77084dc7..b8d82eee 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -255,7 +255,7 @@ enum ImGuiNavHighlightFlags_ { ImGuiNavHighlightFlags_TypeDefault = 1 << 0, ImGuiNavHighlightFlags_TypeThin = 1 << 1, - ImGuiNavHighlightFlags_AlwaysRender = 1 << 2 + ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2 }; enum ImGuiCorner From 2047c58efbc204a8562773286ae2507b4b65b847 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 11 Oct 2017 20:28:16 +0200 Subject: [PATCH 181/319] Nav: Extract part of NavUpdate() into a saner NavScrollToBringItemIntoView() (#787) --- imgui.cpp | 71 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 39c25233..d887cdb0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2611,6 +2611,42 @@ static ImVec2 GetNavInputAmount2d(int stick_no, ImGuiNavReadMode mode, float slo return delta; } +// NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated. +static void NavScrollToBringItemIntoView(ImGuiWindow* window, ImRect& item_rect_rel) +{ + // Scroll to keep newly navigated item fully into view + ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1)); + //g.OverlayDrawList.AddRect(window->Pos + window_rect_rel.Min, window->Pos + window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG] + if (window_rect_rel.Contains(item_rect_rel)) + return; + + ImGuiContext& g = *GImGui; + if (window->ScrollbarX && item_rect_rel.Min.x < window_rect_rel.Min.x) + { + window->ScrollTarget.x = item_rect_rel.Min.x + window->Scroll.x - g.Style.ItemSpacing.x; + window->ScrollTargetCenterRatio.x = 0.0f; + } + else if (window->ScrollbarX && item_rect_rel.Max.x >= window_rect_rel.Max.x) + { + window->ScrollTarget.x = item_rect_rel.Max.x + window->Scroll.x + g.Style.ItemSpacing.x; + window->ScrollTargetCenterRatio.x = 1.0f; + } + if (item_rect_rel.Min.y < window_rect_rel.Min.y) + { + window->ScrollTarget.y = item_rect_rel.Min.y + window->Scroll.y - g.Style.ItemSpacing.y; + window->ScrollTargetCenterRatio.y = 0.0f; + } + else if (item_rect_rel.Max.y >= window_rect_rel.Max.y) + { + window->ScrollTarget.y = item_rect_rel.Max.y + window->Scroll.y + g.Style.ItemSpacing.y; + window->ScrollTargetCenterRatio.y = 1.0f; + } + + // Estimate upcoming scroll so we can offset our relative mouse position so mouse position can be applied immediately (under this block) + ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); + item_rect_rel.Translate(window->Scroll - next_scroll); +} + static void NavUpdate() { ImGuiContext& g = *GImGui; @@ -2634,39 +2670,10 @@ static void NavUpdate() // Process navigation move request if (g.NavMoveRequest && g.NavMoveResultId != 0) { - IM_ASSERT(g.NavWindow); - ImGuiWindow* window = g.NavWindow; - // Scroll to keep newly navigated item fully into view - ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1,1), window->InnerRect.Max - window->Pos + ImVec2(1,1)); - //g.OverlayDrawList.AddRect(window->Pos + window_rect_rel.Min, window->Pos + window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG] - if (g.NavLayer == 0 && !window_rect_rel.Contains(g.NavMoveResultRectRel)) - { - if (window->ScrollbarX && g.NavMoveResultRectRel.Min.x < window_rect_rel.Min.x) - { - window->ScrollTarget.x = g.NavMoveResultRectRel.Min.x + window->Scroll.x - g.Style.ItemSpacing.x; - window->ScrollTargetCenterRatio.x = 0.0f; - } - else if (window->ScrollbarX && g.NavMoveResultRectRel.Max.x >= window_rect_rel.Max.x) - { - window->ScrollTarget.x = g.NavMoveResultRectRel.Max.x + window->Scroll.x + g.Style.ItemSpacing.x; - window->ScrollTargetCenterRatio.x = 1.0f; - } - if (g.NavMoveResultRectRel.Min.y < window_rect_rel.Min.y) - { - window->ScrollTarget.y = g.NavMoveResultRectRel.Min.y + window->Scroll.y - g.Style.ItemSpacing.y; - window->ScrollTargetCenterRatio.y = 0.0f; - } - else if (g.NavMoveResultRectRel.Max.y >= window_rect_rel.Max.y) - { - window->ScrollTarget.y = g.NavMoveResultRectRel.Max.y + window->Scroll.y + g.Style.ItemSpacing.y; - window->ScrollTargetCenterRatio.y = 1.0f; - } - - // Estimate upcoming scroll so we can offset our relative mouse position so mouse position can be applied immediately (under this block) - ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); - g.NavMoveResultRectRel.Translate(window->Scroll - next_scroll); - } + IM_ASSERT(g.NavWindow); + if (g.NavLayer == 0) + NavScrollToBringItemIntoView(g.NavWindow, g.NavMoveResultRectRel); // Apply result from previous frame navigation directional move request ImGui::ClearActiveID(); From f0d437dd9c73f62cfaa7a29e48c64181d79735a3 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 11 Oct 2017 20:29:53 +0200 Subject: [PATCH 182/319] Nav: SetActiveId() uses SetActiveIDNoNav() to avoid duplicating logic. (#787) --- imgui.cpp | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d887cdb0..8d5de35c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1941,30 +1941,6 @@ ImGuiWindow* ImGui::GetParentWindow() return g.CurrentWindowStack[(unsigned int)g.CurrentWindowStack.Size - 2]; } -void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - g.ActiveIdIsJustActivated = (g.ActiveId != id); - g.ActiveId = id; - g.ActiveIdAllowNavDirFlags = 0; - g.ActiveIdAllowOverlap = false; - g.ActiveIdWindow = window; - if (id) - { - g.ActiveIdIsAlive = true; - g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustNavigatedId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; - if (g.ActiveIdSource == ImGuiInputSource_Nav) - g.NavDisableMouseHover = true; - else - g.NavDisableHighlight = true; - g.NavId = id; - if (window) - g.NavLayer = window->DC.NavLayerCurrent; - if (window) // NB: We current assume that SetActiveId() is called in the context where its NavLayer is the current one, which should be the case. - window->NavLastIds[window->DC.NavLayerCurrent] = id; - } -} - void ImGui::SetActiveIDNoNav(ImGuiID id, ImGuiWindow* window) { ImGuiContext& g = *GImGui; @@ -1976,7 +1952,25 @@ void ImGui::SetActiveIDNoNav(ImGuiID id, ImGuiWindow* window) if (id) { g.ActiveIdIsAlive = true; - g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; + g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustNavigatedId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; + } +} + +void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + SetActiveIDNoNav(id, window); + if (id) + { + if (g.ActiveIdSource == ImGuiInputSource_Nav) + g.NavDisableMouseHover = true; + else + g.NavDisableHighlight = true; + g.NavId = id; + if (window) + g.NavLayer = window->DC.NavLayerCurrent; + if (window) // NB: We current assume that SetActiveId() is called in the context where its NavLayer is the current one, which should be the case. + window->NavLastIds[window->DC.NavLayerCurrent] = id; } } From b05b31e6905384c71dbdb223e1ca080cf42033b9 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 11 Oct 2017 20:37:45 +0200 Subject: [PATCH 183/319] Nav: Made NavWindow always refresh from NavId so we can lazily retrieve the window for user functions that don't have it. This is not required by current commit but I'd rather test it earlier. Idea: eventually if we switch to 64-bit identifiers we could reserve e.g. 20 bits to store a simplified window identifier so we can always retrieve a window from an id. (#787) --- imgui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.cpp b/imgui.cpp index 8d5de35c..cb5c8bf3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2224,6 +2224,7 @@ static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGu if (g.NavId == id) { window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; + g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. g.NavIdIsAlive = true; g.NavIdTabCounter = window->FocusIdxTabCounter; } From 17519c313a4f4c01df62a1628f28f0571dec9b1f Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 11 Oct 2017 20:47:41 +0200 Subject: [PATCH 184/319] Nav: Extract part of NavUpdate() into a saner NavUpdateWindowingTarget() (#787) --- imgui.cpp | 156 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 81 insertions(+), 75 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cb5c8bf3..733c04f2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2606,6 +2606,85 @@ static ImVec2 GetNavInputAmount2d(int stick_no, ImGuiNavReadMode mode, float slo return delta; } +// Window management mode (change focus, move/resize window, jump back and forth to menu layer) +static void NavUpdateWindowingTarget() +{ + ImGuiContext& g = *GImGui; + if (!g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiNavReadMode_Pressed)) + { + ImGuiWindow* window = g.NavWindow; + if (!window) + window = FindWindowNavigable(g.Windows.Size - 1, -1, -1); + if (window) + { + g.NavWindowingTarget = window->RootNonPopupWindow; + g.NavWindowingDisplayAlpha = 0.0f; + g.NavWindowingToggleLayer = true; + } + } + if (g.NavWindowingTarget) + { + // Visuals only appears after a brief time holding the button, so that a fast tap (to toggle NavLayer) doesn't add visual noise + const float pressed_duration = g.IO.NavInputsDownDuration[ImGuiNavInput_PadMenu]; + g.NavWindowingDisplayAlpha = ImMax(g.NavWindowingDisplayAlpha, ImSaturate((pressed_duration - 0.20f) / 0.05f)); + g.NavWindowingToggleLayer &= (g.NavWindowingDisplayAlpha < 1.0f); // Once button is held long enough we don't consider it a tag-to-toggle-layer press anymore. + + // Select window to focus + const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_PadFocusPrev, ImGuiNavReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_PadFocusNext, ImGuiNavReadMode_RepeatSlow); + if (focus_change_dir != 0 && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)) + { + const int i_current = FindWindowIndex(g.NavWindowingTarget); + ImGuiWindow* window_target = FindWindowNavigable(i_current + focus_change_dir, -1, focus_change_dir); + if (!window_target) + window_target = FindWindowNavigable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir); + g.NavWindowingTarget = window_target; + g.NavWindowingToggleLayer = false; + g.NavWindowingDisplayAlpha = 1.0f; + } + + // Move window + if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) + { + const ImVec2 move_delta = GetNavInputAmount2d(1, ImGuiNavReadMode_Down); + 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; + g.NavDisableMouseHover = true; + MarkIniSettingsDirty(g.NavWindowingTarget); + } + } + + if (!IsNavInputDown(ImGuiNavInput_PadMenu)) + { + // Apply actual focus only when releasing the NavMenu button (until then the window was merely rendered front-most) + if (g.NavWindowingTarget && !g.NavWindowingToggleLayer && (!g.NavWindow || g.NavWindowingTarget != g.NavWindow->RootNonPopupWindow)) + { + ImGui::FocusWindow(g.NavWindowingTarget); + g.NavDisableHighlight = false; + g.NavDisableMouseHover = true; + if (g.NavWindowingTarget->NavLastIds[0] == 0) + NavInitWindow(g.NavWindowingTarget, false); + } + + // Single press toggles NavLayer + if (g.NavWindowingToggleLayer && g.NavWindow) + { + if ((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) == 0 && (g.NavWindow->RootWindow->DC.NavLayerActiveMask & (1 << 1)) != 0) + ImGui::FocusWindow(g.NavWindow->RootWindow); + g.NavLayer = (g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0; + g.NavDisableHighlight = false; + g.NavDisableMouseHover = true; + if (g.NavLayer == 0 && g.NavWindow->NavLastIds[0] != 0) + SetNavIdAndMoveMouse(g.NavWindow->NavLastIds[0], g.NavLayer, ImRect()); + else + NavInitWindow(g.NavWindow, true); + } + g.NavWindowingTarget = NULL; + } + } +} + // NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated. static void NavScrollToBringItemIntoView(ImGuiWindow* window, ImRect& item_rect_rel) { @@ -2699,83 +2778,10 @@ static void NavUpdate() } g.NavIdIsAlive = false; g.NavJustTabbedId = 0; - - // Navigation windowing mode (change focus, move/resize window) - if (!g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiNavReadMode_Pressed)) - { - ImGuiWindow* window = g.NavWindow; - if (!window) - window = FindWindowNavigable(g.Windows.Size-1, -1, -1); - if (window) - { - g.NavWindowingTarget = window->RootNonPopupWindow; - g.NavWindowingDisplayAlpha = 0.0f; - g.NavWindowingToggleLayer = true; - } - } - if (g.NavWindowingTarget) - { - // Visuals only appears after a brief time holding the button, so that a fast tap (to toggle NavLayer) doesn't add visual noise - const float pressed_duration = g.IO.NavInputsDownDuration[ImGuiNavInput_PadMenu]; - g.NavWindowingDisplayAlpha = ImMax(g.NavWindowingDisplayAlpha, ImSaturate((pressed_duration - 0.20f) / 0.05f)); - g.NavWindowingToggleLayer &= (g.NavWindowingDisplayAlpha < 1.0f); // Once button is held long enough we don't consider it a tag-to-toggle-layer press anymore. - - // Select window to focus - const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_PadFocusPrev, ImGuiNavReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_PadFocusNext, ImGuiNavReadMode_RepeatSlow); - if (focus_change_dir != 0 && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)) - { - const int i_current = FindWindowIndex(g.NavWindowingTarget); - ImGuiWindow* window_target = FindWindowNavigable(i_current + focus_change_dir, -1, focus_change_dir); - if (!window_target) - window_target = FindWindowNavigable((focus_change_dir < 0) ? (g.Windows.Size-1) : 0, i_current, focus_change_dir); - g.NavWindowingTarget = window_target; - g.NavWindowingToggleLayer = false; - g.NavWindowingDisplayAlpha = 1.0f; - } - - // Move window - if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) - { - const ImVec2 move_delta = GetNavInputAmount2d(1, ImGuiNavReadMode_Down); - 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; - g.NavDisableMouseHover = true; - MarkIniSettingsDirty(g.NavWindowingTarget); - } - } - - if (!IsNavInputDown(ImGuiNavInput_PadMenu)) - { - // Apply actual focus only when releasing the NavMenu button (until then the window was merely rendered front-most) - if (g.NavWindowingTarget && !g.NavWindowingToggleLayer && (!g.NavWindow || g.NavWindowingTarget != g.NavWindow->RootNonPopupWindow)) - { - ImGui::FocusWindow(g.NavWindowingTarget); - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - if (g.NavWindowingTarget->NavLastIds[0] == 0) - NavInitWindow(g.NavWindowingTarget, false); - } - - // Single press toggles NavLayer - if (g.NavWindowingToggleLayer && g.NavWindow) - { - if ((g.NavWindow->DC.NavLayerActiveMask & (1<<1)) == 0 && (g.NavWindow->RootWindow->DC.NavLayerActiveMask & (1<<1)) != 0) - ImGui::FocusWindow(g.NavWindow->RootWindow); - g.NavLayer = (g.NavWindow->DC.NavLayerActiveMask & (1<<1)) ? (g.NavLayer ^ 1) : 0; - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - if (g.NavLayer == 0 && g.NavWindow->NavLastIds[0] != 0) - SetNavIdAndMoveMouse(g.NavWindow->NavLastIds[0], g.NavLayer, ImRect()); - else - NavInitWindow(g.NavWindow, true); - } - g.NavWindowingTarget = NULL; - } - } IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); + NavUpdateWindowingTarget(); + // Set output flags for user application g.IO.NavUsable = g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); g.IO.NavActive = (g.IO.NavUsable && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; From 4548bcb5c92ea88fd7c77e14cbc9ebde6b81bd14 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 12 Oct 2017 02:09:13 +0200 Subject: [PATCH 185/319] Nav: commiting shallow bits to reduce noise from working copy/upcoming commit. --- imgui.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 733c04f2..c79284f8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1962,15 +1962,15 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) SetActiveIDNoNav(id, window); if (id) { - if (g.ActiveIdSource == ImGuiInputSource_Nav) - g.NavDisableMouseHover = true; - else - g.NavDisableHighlight = true; g.NavId = id; if (window) g.NavLayer = window->DC.NavLayerCurrent; if (window) // NB: We current assume that SetActiveId() is called in the context where its NavLayer is the current one, which should be the case. window->NavLastIds[window->DC.NavLayerCurrent] = id; + if (g.ActiveIdSource == ImGuiInputSource_Nav) + g.NavDisableMouseHover = true; + else + g.NavDisableHighlight = true; } } @@ -2184,6 +2184,7 @@ static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGu ImGuiContext& g = *GImGui; //if (!g.IO.NavUsable) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag. // return; + const ImGuiItemFlags item_flags = window->DC.ItemFlags; const ImRect nav_bb_rel(nav_bb.Min - g.NavWindow->Pos, nav_bb.Max - g.NavWindow->Pos); if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) @@ -2223,10 +2224,10 @@ static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGu // Update window-relative bounding box of navigated item if (g.NavId == id) { - window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; - g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. + g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. g.NavIdIsAlive = true; g.NavIdTabCounter = window->FocusIdxTabCounter; + window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) } } @@ -2921,7 +2922,7 @@ static void NavUpdate() g.NavMoveResultParentId = 0; g.NavMoveResultDistAxial = g.NavMoveResultDistBox = g.NavMoveResultDistCenter = FLT_MAX; - // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we clamp its bounding box (used for search) to the visible area to restart navigation within visible items + // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0) { ImGuiWindow* window = g.NavWindow; @@ -9031,9 +9032,9 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 select_all = true; } SetActiveID(id, window); + FocusWindow(window); if (!is_multiline) g.ActiveIdAllowNavDirFlags = ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); - FocusWindow(window); } else if (io.MouseClicked[0]) { From 48498b337de330fe91d1faa6b7fbcd154ea28b2f Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 13 Oct 2017 13:07:07 +0200 Subject: [PATCH 186/319] Internals: SetActiveID window cannot be NULL --- imgui.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c79284f8..340da83d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1963,10 +1963,10 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) if (id) { g.NavId = id; - if (window) - g.NavLayer = window->DC.NavLayerCurrent; - if (window) // NB: We current assume that SetActiveId() is called in the context where its NavLayer is the current one, which should be the case. - window->NavLastIds[window->DC.NavLayerCurrent] = id; + + // Assume that SetActiveID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it. + g.NavLayer = window->DC.NavLayerCurrent; + window->NavLastIds[window->DC.NavLayerCurrent] = id; if (g.ActiveIdSource == ImGuiInputSource_Nav) g.NavDisableMouseHover = true; else From 8b095e483b093732b8a46c7b7d97feb3ca665fd2 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 13 Oct 2017 13:07:45 +0200 Subject: [PATCH 187/319] Internals: Moved SetNavID() and renamed casing to be consistent with stuff exposed in imgui_internal.h --- imgui.cpp | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 340da83d..ba882a01 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1941,6 +1941,25 @@ ImGuiWindow* ImGui::GetParentWindow() return g.CurrentWindowStack[(unsigned int)g.CurrentWindowStack.Size - 2]; } +static void SetNavID(ImGuiID id, int nav_layer) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.NavWindow); + IM_ASSERT(nav_layer == 0 || nav_layer == 1); + g.NavId = id; + g.NavWindow->NavLastIds[nav_layer] = g.NavId; +} + +static void SetNavIDAndMoveMouse(ImGuiID id, int nav_layer, const ImRect& rect_rel) +{ + ImGuiContext& g = *GImGui; + SetNavID(id, nav_layer); + g.NavWindow->NavRectRel[nav_layer] = rect_rel; + g.NavMousePosDirty = true; + g.NavDisableHighlight = false; + g.NavDisableMouseHover = true; +} + void ImGui::SetActiveIDNoNav(ImGuiID id, ImGuiWindow* window) { ImGuiContext& g = *GImGui; @@ -2477,25 +2496,6 @@ int ImGui::GetFrameCount() return GImGui->FrameCount; } -static void SetNavId(ImGuiID id, int nav_layer) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavWindow); - IM_ASSERT(nav_layer == 0 || nav_layer == 1); - g.NavId = id; - g.NavWindow->NavLastIds[nav_layer] = g.NavId; -} - -static void SetNavIdAndMoveMouse(ImGuiID id, int nav_layer, const ImRect& rect_rel) -{ - ImGuiContext& g = *GImGui; - SetNavId(id, nav_layer); - g.NavWindow->NavRectRel[nav_layer] = rect_rel; - g.NavMousePosDirty = true; - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; -} - // This needs to be called before we submit any widget (aka in or before Begin) static void NavInitWindow(ImGuiWindow* window, bool force_reinit) { @@ -2507,7 +2507,7 @@ static void NavInitWindow(ImGuiWindow* window, bool force_reinit) init_for_nav = true; if (init_for_nav) { - SetNavId(0, g.NavLayer); + SetNavID(0, g.NavLayer); g.NavInitRequest = true; g.NavInitResultId = 0; g.NavInitResultExplicit = false; @@ -2677,7 +2677,7 @@ static void NavUpdateWindowingTarget() g.NavDisableHighlight = false; g.NavDisableMouseHover = true; if (g.NavLayer == 0 && g.NavWindow->NavLastIds[0] != 0) - SetNavIdAndMoveMouse(g.NavWindow->NavLastIds[0], g.NavLayer, ImRect()); + SetNavIDAndMoveMouse(g.NavWindow->NavLastIds[0], g.NavLayer, ImRect()); else NavInitWindow(g.NavWindow, true); } @@ -2732,7 +2732,7 @@ static void NavUpdate() { // Apply result from previous navigation init request (typically select the first item, unless SetItemDefaultFocus() has been called) IM_ASSERT(g.NavWindow); - SetNavId(g.NavInitResultId, g.NavLayer); + SetNavID(g.NavInitResultId, g.NavLayer); g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel; if (g.NavDisableMouseHover) g.NavMousePosDirty = true; @@ -2752,7 +2752,7 @@ static void NavUpdate() // Apply result from previous frame navigation directional move request ImGui::ClearActiveID(); - SetNavIdAndMoveMouse(g.NavMoveResultId, g.NavLayer, g.NavMoveResultRectRel); + SetNavIDAndMoveMouse(g.NavMoveResultId, g.NavLayer, g.NavMoveResultRectRel); g.NavJustNavigatedId = g.NavMoveResultId; g.NavMoveFromClampedRefRect = false; } @@ -2801,7 +2801,7 @@ static void NavUpdate() ImGuiWindow* parent_window = g.NavWindow->ParentWindow; ImGui::FocusWindow(parent_window); IM_ASSERT(child_window->ChildId != 0); - SetNavId(child_window->ChildId, g.NavLayer); // FIXME-NAV: Layer not necessarily correct + SetNavID(child_window->ChildId, g.NavLayer); // FIXME-NAV: Layer not necessarily correct g.NavIdIsAlive = false; if (g.NavDisableMouseHover) g.NavMousePosDirty = true; @@ -2816,7 +2816,7 @@ static void NavUpdate() // Leave the "menu" layer g.NavLayer = 0; if (g.NavWindow->NavLastIds[0]) - SetNavIdAndMoveMouse(g.NavWindow->NavLastIds[0], g.NavLayer, ImRect()); + SetNavIDAndMoveMouse(g.NavWindow->NavLastIds[0], g.NavLayer, ImRect()); else NavInitWindow(g.NavWindow, true); } @@ -9918,7 +9918,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (!g.NavDisableMouseHover && g.NavWindow == window) { g.NavDisableHighlight = true; - SetNavId(id, window->DC.NavLayerCurrent); + SetNavID(id, window->DC.NavLayerCurrent); } // Render @@ -10154,7 +10154,7 @@ void ImGui::EndMenuBar() // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost) IM_ASSERT(window->DC.NavLayerActiveMaskNext & 0x02); // Sanity check FocusWindow(window); - SetNavIdAndMoveMouse(window->NavLastIds[1], 1, window->NavRectRel[1]); + SetNavIDAndMoveMouse(window->NavLastIds[1], 1, window->NavRectRel[1]); g.NavLayer = 1; g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. g.NavMoveRequestForwardStep = 1; From 03712192223b80bd86950738948157dc60531971 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 13 Oct 2017 13:23:21 +0200 Subject: [PATCH 188/319] Nav: Internals: Renamed SetActiveIDNoNav -> SetActiveID() and old SetActiveID() -> SetFocusID(), both functions needs to be called when both are desirabled. (#787) May break code relying on imgui_internal.h, relying on nav and not calling ButtonBehavior(). --- imgui.cpp | 44 ++++++++++++++++++++++---------------------- imgui_internal.h | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ba882a01..66f736f7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1960,7 +1960,7 @@ static void SetNavIDAndMoveMouse(ImGuiID id, int nav_layer, const ImRect& rect_r g.NavDisableMouseHover = true; } -void ImGui::SetActiveIDNoNav(ImGuiID id, ImGuiWindow* window) +void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) { ImGuiContext& g = *GImGui; g.ActiveIdIsJustActivated = (g.ActiveId != id); @@ -1975,22 +1975,18 @@ void ImGui::SetActiveIDNoNav(ImGuiID id, ImGuiWindow* window) } } -void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) +// Assume that SetFocusID() is called in the context where its NavLayer is the current window nav layer. +void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) { + IM_ASSERT(id != 0); ImGuiContext& g = *GImGui; - SetActiveIDNoNav(id, window); - if (id) - { - g.NavId = id; - - // Assume that SetActiveID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it. - g.NavLayer = window->DC.NavLayerCurrent; - window->NavLastIds[window->DC.NavLayerCurrent] = id; - if (g.ActiveIdSource == ImGuiInputSource_Nav) - g.NavDisableMouseHover = true; - else - g.NavDisableHighlight = true; - } + g.NavId = id; + g.NavLayer = window->DC.NavLayerCurrent; + window->NavLastIds[window->DC.NavLayerCurrent] = id; + if (g.ActiveIdSource == ImGuiInputSource_Nav) + g.NavDisableMouseHover = true; + else + g.NavDisableHighlight = true; } void ImGui::ClearActiveID() @@ -3535,7 +3531,7 @@ void ImGui::EndFrame() { g.MovedWindow = g.HoveredWindow; g.MovedWindowMoveId = g.HoveredWindow->MoveId; - SetActiveIDNoNav(g.MovedWindowMoveId, g.HoveredRootWindow); + SetActiveID(g.MovedWindowMoveId, g.HoveredRootWindow); } } else if (g.NavWindow != NULL && GetFrontMostModalRootWindow() == NULL) @@ -4616,7 +4612,7 @@ static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b { ImGui::FocusWindow(child_window); NavInitWindow(child_window, false); - ImGui::SetActiveIDNoNav(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item + ImGui::SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item g.ActiveIdSource = ImGuiInputSource_Nav; } @@ -6718,10 +6714,9 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // FIXME-NAVIGATION: We don't honor those different behaviors. if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0]) { - if (flags & ImGuiButtonFlags_NoNavOverride) - SetActiveIDNoNav(id, window); - else - SetActiveID(id, window); // Hold on ID + SetActiveID(id, window); + if (!(flags & ImGuiButtonFlags_NoNavOverride)) + SetFocusID(id, window); FocusWindow(window); g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; } @@ -6764,6 +6759,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. g.NavActivateId = id; // This is so SetActiveId assign a Nav source SetActiveID(id, window); + SetFocusID(id, window); g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); } } @@ -7607,7 +7603,7 @@ bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen) // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id - SetActiveIDNoNav(g.ScalarAsInputTextId, window); + SetActiveID(g.ScalarAsInputTextId, window); g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); SetHoveredID(0); FocusableItemUnregister(window); @@ -7876,6 +7872,7 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) { SetActiveID(id, window); + SetFocusID(id, window); FocusWindow(window); if (tab_focus_requested || g.IO.KeyCtrl || g.NavInputId == id) { @@ -7927,6 +7924,7 @@ bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) { SetActiveID(id, window); + SetFocusID(id, window); FocusWindow(window); } @@ -8177,6 +8175,7 @@ bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, f if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || g.NavInputId == id) { SetActiveID(id, window); + SetFocusID(id, window); FocusWindow(window); if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id) { @@ -9032,6 +9031,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 select_all = true; } SetActiveID(id, window); + SetFocusID(id, window); FocusWindow(window); if (!is_multiline) g.ActiveIdAllowNavDirFlags = ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); diff --git a/imgui_internal.h b/imgui_internal.h index b8d82eee..f7994ae1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -865,7 +865,7 @@ namespace ImGui IMGUI_API void EndFrame(); // Ends the ImGui frame. Automatically called by Render()! you most likely don't need to ever call that yourself directly. If you don't need to render you can call EndFrame() but you'll have wasted CPU already. If you don't need to render, don't create any windows instead! IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); - IMGUI_API void SetActiveIDNoNav(ImGuiID id, ImGuiWindow* window); + IMGUI_API void SetFocusID(ImGuiID id, ImGuiWindow* window); IMGUI_API void ClearActiveID(); IMGUI_API void SetHoveredID(ImGuiID id); IMGUI_API void KeepAliveID(ImGuiID id); From b667d5a9e745bd79ac723da157602b51b9e2dee4 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 13 Oct 2017 13:26:02 +0200 Subject: [PATCH 189/319] Nav: Internals: Renamed ImGuiButtonFlags_NoNavOverride to ImGuiButtonFlags_NoNavFocus + fixed a theorically missing test in ButtonBehavior() (#787) --- imgui.cpp | 11 ++++++----- imgui_internal.h | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 66f736f7..92cce915 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5214,7 +5214,7 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us const ImRect resize_rect(br - ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f), br); const ImGuiID resize_id = window->GetID("#RESIZE"); bool hovered, held; - ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds | ImGuiButtonFlags_NoNavOverride); + ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds | ImGuiButtonFlags_NoNavFocus); if (hovered || held) g.MouseCursor = ImGuiMouseCursor_ResizeNWSE; @@ -5567,7 +5567,7 @@ static void Scrollbar(ImGuiWindow* window, bool horizontal) bool held = false; bool hovered = false; const bool previously_held = (g.ActiveId == id); - ImGui::ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavOverride); + ImGui::ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v); float scroll_ratio = ImSaturate(scroll_v / scroll_max); @@ -6715,7 +6715,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0]) { SetActiveID(id, window); - if (!(flags & ImGuiButtonFlags_NoNavOverride)) + if (!(flags & ImGuiButtonFlags_NoNavFocus)) SetFocusID(id, window); FocusWindow(window); g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; @@ -6759,7 +6759,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. g.NavActivateId = id; // This is so SetActiveId assign a Nav source SetActiveID(id, window); - SetFocusID(id, window); + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + SetFocusID(id, window); g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); } } @@ -6780,7 +6781,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool pressed = true; ClearActiveID(); } - if (!(flags & ImGuiButtonFlags_NoNavOverride)) + if (!(flags & ImGuiButtonFlags_NoNavFocus)) g.NavDisableHighlight = true; } else if (g.ActiveIdSource == ImGuiInputSource_Nav) diff --git a/imgui_internal.h b/imgui_internal.h index f7994ae1..967b4416 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -182,7 +182,7 @@ enum ImGuiButtonFlags_ ImGuiButtonFlags_AlignTextBaseLine = 1 << 8, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine ImGuiButtonFlags_NoKeyModifiers = 1 << 9, // disable interaction if a key modifier is held ImGuiButtonFlags_AllowOverlapMode = 1 << 10, // require previous frame HoveredId to either match id or be null before being usable - ImGuiButtonFlags_NoNavOverride = 1 << 11 // don't override navigation id when activated + ImGuiButtonFlags_NoNavFocus = 1 << 11 // don't override navigation focus when activated }; enum ImGuiSliderFlags_ From f852b9a52aeff7978bc3d2c5b76d5ffeb57b0f4d Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 13 Oct 2017 13:29:42 +0200 Subject: [PATCH 190/319] Exposed Scrollbar() in imgui_internal.h and removed a bool arg --- imgui.cpp | 18 ++++++++++-------- imgui_internal.h | 1 + 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 92cce915..9556a8bf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -644,7 +644,6 @@ static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs); static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags); static void ClearSetNextWindowData(); static void CheckStacksSize(ImGuiWindow* window, bool write); -static void Scrollbar(ImGuiWindow* window, bool horizontal); static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window); static void AddDrawListToRenderList(ImVector& out_render_list, ImDrawList* draw_list); @@ -5299,9 +5298,9 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us // Scrollbars if (window->ScrollbarX) - Scrollbar(window, true); + Scrollbar(ImGuiLayoutType_Horizontal); if (window->ScrollbarY) - Scrollbar(window, false); + Scrollbar(ImGuiLayoutType_Vertical); // Render resize grip // (after the input handling so we don't have a frame of latency) @@ -5522,9 +5521,12 @@ void ImGui::End() // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. -static void Scrollbar(ImGuiWindow* window, bool horizontal) +void ImGui::Scrollbar(ImGuiLayoutType direction) { ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + const bool horizontal = (direction == ImGuiLayoutType_Horizontal); const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY"); @@ -5547,7 +5549,7 @@ static void Scrollbar(ImGuiWindow* window, bool horizontal) window_rounding_corners = ImGuiCorner_BotLeft | (other_scrollbar ? 0 : ImGuiCorner_BotRight); else window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImGuiCorner_TopRight : 0) | (other_scrollbar ? 0 : ImGuiCorner_BotRight); - window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_ScrollbarBg), window_rounding, window_rounding_corners); + window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window_rounding, window_rounding_corners); bb.Expand(ImVec2(-ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f))); // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar) @@ -5567,7 +5569,7 @@ static void Scrollbar(ImGuiWindow* window, bool horizontal) bool held = false; bool hovered = false; const bool previously_held = (g.ActiveId == id); - ImGui::ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); + ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v); float scroll_ratio = ImSaturate(scroll_v / scroll_max); @@ -5580,7 +5582,7 @@ static void Scrollbar(ImGuiWindow* window, bool horizontal) // Click position in scrollbar normalized space (0.0f->1.0f) const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); - ImGui::SetHoveredID(id); + SetHoveredID(id); bool seek_absolute = false; if (!previously_held) @@ -5616,7 +5618,7 @@ static void Scrollbar(ImGuiWindow* window, bool horizontal) } // Render - const ImU32 grab_col = ImGui::GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab); + const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab); if (horizontal) window->DrawList->AddRectFilled(ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y), ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y), grab_col, style.ScrollbarRounding); else diff --git a/imgui_internal.h b/imgui_internal.h index 967b4416..2528b2ec 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -890,6 +890,7 @@ namespace ImGui IMGUI_API int CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate); + IMGUI_API void Scrollbar(ImGuiLayoutType direction); IMGUI_API void VerticalSeparator(); // Vertical separator, for menu bars (use current line height). not exposed because it is misleading what it doesn't have an effect on regular layout. // FIXME-WIP: New Columns API From 53780a4fcc5344d0007eb49e9f231dc8954ec0c2 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 13 Oct 2017 16:23:44 +0200 Subject: [PATCH 191/319] Nav: Shallow tweaks. --- imgui.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9556a8bf..20610e6a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2890,7 +2890,7 @@ static void NavUpdate() // *Fallback* manual-scroll with NavUp/NavDown when window has no navigable item ImGuiWindow* window = g.NavWindow; const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. - if (!window->DC.NavLayerActiveMask && window->DC.NavHasScroll && g.NavMoveRequest) + if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) { if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); @@ -6753,10 +6753,11 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool } if (g.NavActivateDownId == id) { - bool nav_pressed = (g.NavActivateId == id) || IsNavInputPressed(ImGuiNavInput_PadActivate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiNavReadMode_Repeat : ImGuiNavReadMode_Pressed); - if (nav_pressed) + bool nav_activated_by_code = (g.NavActivateId == id); + bool nav_activated_by_inputs = IsNavInputPressed(ImGuiNavInput_PadActivate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiNavReadMode_Repeat : ImGuiNavReadMode_Pressed); + if (nav_activated_by_code || nav_activated_by_inputs) pressed = true; - if (nav_pressed || g.ActiveId == id) + if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id) { // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. g.NavActivateId = id; // This is so SetActiveId assign a Nav source From c42baf392a37e7621f473a16a60031b4a3a62022 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 13 Oct 2017 16:48:14 +0200 Subject: [PATCH 192/319] Reluctantly exposed GetActiveID(), GetHoveredID() in imgui_internal because the demo code will need it. --- imgui.cpp | 12 ++++++++++++ imgui_internal.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 20610e6a..e856846a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1974,6 +1974,12 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) } } +ImGuiID ImGui::GetActiveID() +{ + ImGuiContext& g = *GImGui; + return g.ActiveId; +} + // Assume that SetFocusID() is called in the context where its NavLayer is the current window nav layer. void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) { @@ -2000,6 +2006,12 @@ void ImGui::SetHoveredID(ImGuiID id) g.HoveredIdAllowOverlap = false; } +ImGuiID ImGui::GetHoveredID() +{ + ImGuiContext& g = *GImGui; + return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame; +} + void ImGui::KeepAliveID(ImGuiID id) { ImGuiContext& g = *GImGui; diff --git a/imgui_internal.h b/imgui_internal.h index 2528b2ec..7f9ee7a1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -865,9 +865,11 @@ namespace ImGui IMGUI_API void EndFrame(); // Ends the ImGui frame. Automatically called by Render()! you most likely don't need to ever call that yourself directly. If you don't need to render you can call EndFrame() but you'll have wasted CPU already. If you don't need to render, don't create any windows instead! IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); + IMGUI_API ImGuiID GetActiveID(); IMGUI_API void SetFocusID(ImGuiID id, ImGuiWindow* window); IMGUI_API void ClearActiveID(); IMGUI_API void SetHoveredID(ImGuiID id); + IMGUI_API ImGuiID GetHoveredID(); IMGUI_API void KeepAliveID(ImGuiID id); IMGUI_API void ItemSize(const ImVec2& size, float text_offset_y = 0.0f); From db5f1b79cdb31d50d302f7052a8f25102ecfef57 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 13 Oct 2017 19:18:59 +0200 Subject: [PATCH 193/319] Nav: ButtonBehavior() keep _displaying_ NavId item as howered when refocusing or moving window, which is consistent with IsItemHovered() and reduce noise/flicker (#787) --- imgui.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e856846a..fd16860c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2205,6 +2205,7 @@ static void NavMoveRequestCancel() NavUpdateAnyRequestFlag(); } +// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) { ImGuiContext& g = *GImGui; @@ -6758,11 +6759,10 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool } // Gamepad/Keyboard navigation - if (g.NavId == id && !g.NavDisableHighlight && (g.ActiveId == 0 || g.ActiveId == id)) - { - // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse + // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse. + if (g.NavId == id && !g.NavDisableHighlight && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId)) hovered = true; - } + if (g.NavActivateDownId == id) { bool nav_activated_by_code = (g.NavActivateId == id); From d761825cfb92ed4d5ce8fa5abfc59f6c20d77023 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 13 Oct 2017 22:33:06 +0200 Subject: [PATCH 194/319] Nav: Renaming a field + Comments. --- imgui.cpp | 6 +++--- imgui.h | 4 ++-- imgui_internal.h | 13 +++++++------ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index fd16860c..454457f9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1970,7 +1970,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) if (id) { g.ActiveIdIsAlive = true; - g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustNavigatedId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; + g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; } } @@ -2748,7 +2748,7 @@ static void NavUpdate() g.NavInitRequest = false; g.NavInitResultExplicit = false; g.NavInitResultId = 0; - g.NavJustNavigatedId = 0; + g.NavJustMovedToId = 0; // Process navigation move request if (g.NavMoveRequest && g.NavMoveResultId != 0) @@ -2761,7 +2761,7 @@ static void NavUpdate() // Apply result from previous frame navigation directional move request ImGui::ClearActiveID(); SetNavIDAndMoveMouse(g.NavMoveResultId, g.NavLayer, g.NavMoveResultRectRel); - g.NavJustNavigatedId = g.NavMoveResultId; + g.NavJustMovedToId = g.NavMoveResultId; g.NavMoveFromClampedRefRect = false; } diff --git a/imgui.h b/imgui.h index 8c6b8f44..0a69e924 100644 --- a/imgui.h +++ b/imgui.h @@ -902,8 +902,8 @@ struct ImGuiIO bool WantCaptureKeyboard; // Widget is active (= ImGui will use your keyboard input). Use to hide keyboard from the rest of your application bool WantTextInput; // Some text input widget is active, which will read input characters from the InputCharacters array. Use to activate on screen keyboard if your system needs one bool WantMoveMouse; // MousePos has been altered. back-end should reposition mouse on next frame. used only if 'NavMovesMouse=true'. - bool NavUsable; // Directional navigation is currently allowed (ImGuiKey_NavXXX events). - bool NavActive; // Directional navigation is active/visible and currently allowed (ImGuiKey_NavXXX events). + bool NavUsable; // Directional navigation is currently allowed (will handle ImGuiKey_NavXXX events). + bool NavActive; // Directional navigation is active/visible and currently allowed (will handle ImGuiKey_NavXXX events). float Framerate; // Application framerate estimation, in frame per second. Solely for convenience. Rolling average estimation based on IO.DeltaTime over 120 frames int MetricsAllocs; // Number of active memory allocations int MetricsRenderVertices; // Vertices output during last call to Render() diff --git a/imgui_internal.h b/imgui_internal.h index 7f9ee7a1..fd646eff 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -463,13 +463,14 @@ struct ImGuiContext ImVector CurrentPopupStack; // Which level of BeginPopup() we are in (reset every frame) // Navigation data (for gamepad/keyboard) - ImGuiWindow* NavWindow; // Nav/focused window for navigation - ImGuiID NavId; // Nav/focused item for navigation - ImGuiID NavActivateId, NavActivateDownId; // ~~ IsNavInputPressed(ImGuiNavInput_PadActivate) ? NavId : 0, etc. - ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_PadInput) ? NavId : 0, etc. + ImGuiWindow* NavWindow; // Focused window for navigation + ImGuiID NavId; // Focused item for navigation + ImGuiID NavActivateId; // ~~ IsNavInputPressed(ImGuiNavInput_PadActivate) ? NavId : 0, also set when calling ActivateItem() + ImGuiID NavActivateDownId; // ~~ IsNavInputPressed(ImGuiNavInput_PadActivate) ? NavId : 0 + ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_PadInput) ? NavId : 0 ImGuiID NavJustTabbedId; // Just tabbed to this id. - ImGuiID NavJustNavigatedId; // Just navigated to this id (result of a successfully MoveRequest) ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame + ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest) ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring. ImGuiWindow* NavWindowingTarget; float NavWindowingDisplayAlpha; @@ -586,7 +587,7 @@ struct ImGuiContext NavWindow = NULL; NavId = NavActivateId = NavActivateDownId = NavInputId = 0; - NavJustTabbedId = NavJustNavigatedId = NavNextActivateId = 0; + NavJustTabbedId = NavJustMovedToId = NavNextActivateId = 0; NavScoringRectScreen = ImRect(); NavWindowingTarget = NULL; NavWindowingDisplayAlpha = 0.0f; From 7d142622028108314d174af8025dacb368ef5605 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 16 Oct 2017 00:33:30 +0200 Subject: [PATCH 195/319] Nav: SetFocusID() update NavWindow and NavRectRel just as we get them. Needed by upcoming commit, committing separately as I'm curious if it has any side-effect. (#787) --- imgui.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4a1bf07d..39e55a57 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1980,14 +1980,20 @@ ImGuiID ImGui::GetActiveID() return g.ActiveId; } -// Assume that SetFocusID() is called in the context where its NavLayer is the current window nav layer. void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) { IM_ASSERT(id != 0); ImGuiContext& g = *GImGui; + + // Assume that SetActiveID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it. + const int nav_layer = window->DC.NavLayerCurrent; g.NavId = id; - g.NavLayer = window->DC.NavLayerCurrent; - window->NavLastIds[window->DC.NavLayerCurrent] = id; + g.NavWindow = window; + g.NavLayer = nav_layer; + window->NavLastIds[nav_layer] = id; + if (window->DC.LastItemId == id) + window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos); + if (g.ActiveIdSource == ImGuiInputSource_Nav) g.NavDisableMouseHover = true; else From 97851f73764c1b0f77764ff850e59a31e025b722 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 16 Oct 2017 00:36:31 +0200 Subject: [PATCH 196/319] Nav: NavProcessItem() computes window-relative rectangle using own Window rather than NavWindow. Not sure what it may mean for child-flattened-nav (disabled). (#787) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 39e55a57..825db0d5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2219,7 +2219,7 @@ static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGu // return; const ImGuiItemFlags item_flags = window->DC.ItemFlags; - const ImRect nav_bb_rel(nav_bb.Min - g.NavWindow->Pos, nav_bb.Max - g.NavWindow->Pos); + const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) { // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback From ae1866c4241eed391445d51cf5a993197665860c Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 16 Oct 2017 00:38:42 +0200 Subject: [PATCH 197/319] Nav: NavProcessItem() updates current NavLayer. Basically we're hearing toward rebuilidng nav info from an id. (#787). --- imgui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.cpp b/imgui.cpp index 825db0d5..4a4c22d1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2258,6 +2258,7 @@ static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGu if (g.NavId == id) { g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. + g.NavLayer = window->DC.NavLayerCurrent; g.NavIdIsAlive = true; g.NavIdTabCounter = window->FocusIdxTabCounter; window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) From b70c2fa8879bf24b6bdd7c87a87eca1ff1fdd389 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 16 Oct 2017 00:46:23 +0200 Subject: [PATCH 198/319] Nav: Internals: Moved some internal code to namespace, comments --- imgui.cpp | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4a4c22d1..abcbd54a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -672,6 +672,13 @@ static inline void DataTypeFormatString(ImGuiDataType data_type, void* data static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2); static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format); +namespace ImGui +{ +static void NavUpdate(); +static void NavUpdateWindowing(); +static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id); +} + //----------------------------------------------------------------------------- // Platform dependent default implementations //----------------------------------------------------------------------------- @@ -2212,7 +2219,7 @@ static void NavMoveRequestCancel() } // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) -static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) +static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) { ImGuiContext& g = *GImGui; //if (!g.IO.NavUsable) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag. @@ -2623,7 +2630,7 @@ static ImVec2 GetNavInputAmount2d(int stick_no, ImGuiNavReadMode mode, float slo } // Window management mode (change focus, move/resize window, jump back and forth to menu layer) -static void NavUpdateWindowingTarget() +static void ImGui::NavUpdateWindowing() { ImGuiContext& g = *GImGui; if (!g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiNavReadMode_Pressed)) @@ -2676,7 +2683,7 @@ static void NavUpdateWindowingTarget() // Apply actual focus only when releasing the NavMenu button (until then the window was merely rendered front-most) if (g.NavWindowingTarget && !g.NavWindowingToggleLayer && (!g.NavWindow || g.NavWindowingTarget != g.NavWindow->RootNonPopupWindow)) { - ImGui::FocusWindow(g.NavWindowingTarget); + FocusWindow(g.NavWindowingTarget); g.NavDisableHighlight = false; g.NavDisableMouseHover = true; if (g.NavWindowingTarget->NavLastIds[0] == 0) @@ -2687,7 +2694,7 @@ static void NavUpdateWindowingTarget() if (g.NavWindowingToggleLayer && g.NavWindow) { if ((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) == 0 && (g.NavWindow->RootWindow->DC.NavLayerActiveMask & (1 << 1)) != 0) - ImGui::FocusWindow(g.NavWindow->RootWindow); + FocusWindow(g.NavWindow->RootWindow); g.NavLayer = (g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0; g.NavDisableHighlight = false; g.NavDisableMouseHover = true; @@ -2737,7 +2744,7 @@ static void NavScrollToBringItemIntoView(ImGuiWindow* window, ImRect& item_rect_ item_rect_rel.Translate(window->Scroll - next_scroll); } -static void NavUpdate() +static void ImGui::NavUpdate() { ImGuiContext& g = *GImGui; g.IO.WantMoveMouse = false; @@ -2745,7 +2752,7 @@ static void NavUpdate() // Process navigation init request (select first/default focus) if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitResultExplicit)) { - // Apply result from previous navigation init request (typically select the first item, unless SetItemDefaultFocus() has been called) + // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) IM_ASSERT(g.NavWindow); SetNavID(g.NavInitResultId, g.NavLayer); g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel; @@ -2766,7 +2773,7 @@ static void NavUpdate() NavScrollToBringItemIntoView(g.NavWindow, g.NavMoveResultRectRel); // Apply result from previous frame navigation directional move request - ImGui::ClearActiveID(); + ClearActiveID(); SetNavIDAndMoveMouse(g.NavMoveResultId, g.NavLayer, g.NavMoveResultRectRel); g.NavJustMovedToId = g.NavMoveResultId; g.NavMoveFromClampedRefRect = false; @@ -2796,7 +2803,7 @@ static void NavUpdate() g.NavJustTabbedId = 0; IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); - NavUpdateWindowingTarget(); + NavUpdateWindowing(); // Set output flags for user application g.IO.NavUsable = g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); @@ -2807,14 +2814,14 @@ static void NavUpdate() { if (g.ActiveId != 0) { - ImGui::ClearActiveID(); + ClearActiveID(); } else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) { // Exit child window ImGuiWindow* child_window = g.NavWindow; ImGuiWindow* parent_window = g.NavWindow->ParentWindow; - ImGui::FocusWindow(parent_window); + FocusWindow(parent_window); IM_ASSERT(child_window->ChildId != 0); SetNavID(child_window->ChildId, g.NavLayer); // FIXME-NAV: Layer not necessarily correct g.NavIdIsAlive = false; @@ -2919,6 +2926,7 @@ static void NavUpdate() } // *Normal* Manual scroll with NavScrollXXX keys + // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. ImVec2 scroll_dir = GetNavInputAmount2d(1, ImGuiNavReadMode_Down, 1.0f/10.0f, 10.0f); if (scroll_dir.x != 0.0f && window->ScrollbarX) { @@ -2974,7 +2982,7 @@ void ImGui::NewFrame() // Initialize on first frame if (!g.Initialized) - ImGui::Initialize(); + Initialize(); SetCurrentFont(GetDefaultFont()); IM_ASSERT(g.Font->IsLoaded()); @@ -3204,8 +3212,8 @@ void ImGui::NewFrame() // Create implicit window - we will only render it if the user has added something to it. // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. - ImGui::SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver); - ImGui::Begin("Debug##Default"); + SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver); + Begin("Debug##Default"); } void ImGui::Initialize() From 82a27fd3aaf78cafc4bec2ef18729f225ad8fefb Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 16 Oct 2017 02:06:42 +0200 Subject: [PATCH 199/319] Nav: InputText: Fixed using Up/Down history callback feature when Nav is enabled (#787) --- imgui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index abcbd54a..2d789695 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9094,6 +9094,8 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Down the line we should have a cleaner library-wide concept of Selected vs Active. g.ActiveIdAllowOverlap = !io.MouseDown[0]; g.WantTextInputNextFrame = 1; + if (flags & ImGuiInputTextFlags_CallbackHistory) + g.ActiveIdAllowNavDirFlags &= ~((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); // Edit in progress const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX; From 7ea52ac1e41996f48bcd3de74bf32e6603c0242e Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 16 Oct 2017 02:30:59 +0200 Subject: [PATCH 200/319] Nav: InputTextMultiline: Fixed navigation/selection. Disabled selecting all when activating a multi-line text editor. (#787) --- imgui.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 2d789695..ffa2b100 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4686,6 +4686,7 @@ void ImGui::EndChild() } else { + // Not navigable into ItemAdd(bb, 0); } } @@ -8974,6 +8975,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 ImGuiWindow* draw_window = window; if (is_multiline) { + ItemAdd(total_bb, id, &frame_bb); if (!BeginChildFrame(id, frame_bb.GetSize())) { EndChildFrame(); @@ -9022,7 +9024,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 bool clear_active_id = false; - bool select_all = (g.ActiveId != id) && (((flags & ImGuiInputTextFlags_AutoSelectAll) != 0) || (g.NavInputId == id)); + bool select_all = (g.ActiveId != id) && (((flags & ImGuiInputTextFlags_AutoSelectAll) != 0) || (g.NavInputId == id)) && (!is_multiline); if (focus_requested || user_clicked || user_scrolled || g.NavInputId == id) { if (g.ActiveId != id) From af565ea82810b89c2e1c317be8a7c91bd8014d5e Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 16 Oct 2017 02:41:28 +0200 Subject: [PATCH 201/319] Nav: InputTextMultiline: Fixed navigation/selection. Disabled selecting all when activating a multi-line text editor. (#787) --- imgui.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ffa2b100..94e49b72 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9066,8 +9066,8 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); - if (!is_multiline) - g.ActiveIdAllowNavDirFlags = ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); + if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory)) + g.ActiveIdAllowNavDirFlags |= ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); } else if (io.MouseClicked[0]) { @@ -9096,8 +9096,6 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Down the line we should have a cleaner library-wide concept of Selected vs Active. g.ActiveIdAllowOverlap = !io.MouseDown[0]; g.WantTextInputNextFrame = 1; - if (flags & ImGuiInputTextFlags_CallbackHistory) - g.ActiveIdAllowNavDirFlags &= ~((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); // Edit in progress const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX; From 29a652adeec8e9e0dafb5f79f06769ad3442ea3a Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 18 Oct 2017 19:51:32 +0200 Subject: [PATCH 202/319] CollapsingHeader(bool*) variant: fixed for IsItemHovered() to work properly in the nav branch.Basically the close button now has to use ItemAdd() to be navable into, which overwrite the IsItemHovered data. (#600, #787) --- imgui.cpp | 3 +++ imgui_demo.cpp | 2 ++ imgui_internal.h | 11 +++++++++++ 3 files changed, 16 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index acd202c2..1114c79c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7346,8 +7346,11 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. ImGuiContext& g = *GImGui; float button_sz = g.FontSize * 0.5f; + ImGuiItemHoveredDataBackup last_item_backup; + last_item_backup.Backup(); if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_sz, window->DC.LastItemRect.Min.y + g.Style.FramePadding.y + button_sz), button_sz)) *p_open = false; + last_item_backup.Restore(); } return is_open; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ac6ff98e..c3402c2f 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -460,11 +460,13 @@ void ImGui::ShowTestWindow(bool* p_open) ImGui::Checkbox("Enable extra group", &closable_group); if (ImGui::CollapsingHeader("Header")) { + ImGui::Text("IsItemHovered: %d", IsItemHovered()); for (int i = 0; i < 5; i++) ImGui::Text("Some content %d", i); } if (ImGui::CollapsingHeader("Header with a close button", &closable_group)) { + ImGui::Text("IsItemHovered: %d", IsItemHovered()); for (int i = 0; i < 5; i++) ImGui::Text("More content %d", i); } diff --git a/imgui_internal.h b/imgui_internal.h index caca4d6c..6bec13d5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -848,6 +848,17 @@ public: ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } }; +// Backup and restore just enough data to be able to use IsItemHovered() on item A after another B in the same window has overwritten the data. +struct ImGuiItemHoveredDataBackup +{ + ImGuiID LastItemId; + ImRect LastItemRect; + bool LastItemRectHoveredRect; + + void Backup() { ImGuiWindow* window = GImGui->CurrentWindow; LastItemId = window->DC.LastItemId; LastItemRect = window->DC.LastItemRect; LastItemRectHoveredRect = window->DC.LastItemRectHoveredRect; } + void Restore() { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemRect = LastItemRect; window->DC.LastItemRectHoveredRect = LastItemRectHoveredRect; } +}; + //----------------------------------------------------------------------------- // Internal API // No guarantee of forward compatibility here. From 0ea66dc260522b4b4c3cecc508242a7555cdc8fc Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 20 Oct 2017 18:16:53 +0200 Subject: [PATCH 203/319] Fixed bad merge from Master --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index dd26f13a..b7c7ac54 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2383,7 +2383,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) return false; if (!IsMouseHoveringRect(bb.Min, bb.Max)) return false; - if (!g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_Default)) + if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_Default)) return false; SetHoveredID(id); From 23b9060468731259f449f3efb0d1d6c531c02b59 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 20 Oct 2017 20:35:39 +0200 Subject: [PATCH 204/319] Nav: Honoring ImGuiItemFlags_NoNav which is used by color picker (#787) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 54097f5f..446fa59e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2275,7 +2275,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con } // Scoring for navigation - if (g.NavId != id) + if (g.NavId != id && !(item_flags & ImGuiItemFlags_NoNav)) { #if IMGUI_DEBUG_NAV // [DEBUG] Score all items in NavWindow at all times From 5fa81f2a26636d5fb238aa0bd57c2d7ea9d205bf Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 20 Oct 2017 22:12:10 +0200 Subject: [PATCH 205/319] Nav: Added ImGuiNavInput_KeyMenu aside from ImGuiNavInput_PadMenu as it is one differenciator between pad and keyboard that's very annoying with the keyboard. Remove the move/resize behavior that appears than holding the button for a while. (#787) --- imgui.cpp | 4 ++-- imgui.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8cc1eca6..62d4e8f9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2665,7 +2665,7 @@ static ImVec2 GetNavInputAmount2d(int stick_no, ImGuiNavReadMode mode, float slo static void ImGui::NavUpdateWindowing() { ImGuiContext& g = *GImGui; - if (!g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiNavReadMode_Pressed)) + if (!g.NavWindowingTarget && (IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiNavReadMode_Pressed) || IsNavInputPressed(ImGuiNavInput_KeyMenu, ImGuiNavReadMode_Pressed))) { ImGuiWindow* window = g.NavWindow; if (!window) @@ -2710,7 +2710,7 @@ static void ImGui::NavUpdateWindowing() } } - if (!IsNavInputDown(ImGuiNavInput_PadMenu)) + if (!IsNavInputDown(ImGuiNavInput_PadMenu) && !IsNavInputDown(ImGuiNavInput_KeyMenu)) { // Apply actual focus only when releasing the NavMenu button (until then the window was merely rendered front-most) if (g.NavWindowingTarget && !g.NavWindowingToggleLayer && (!g.NavWindow || g.NavWindowingTarget != g.NavWindow->RootNonPopupWindow)) diff --git a/imgui.h b/imgui.h index bbcac19d..3e2ab628 100644 --- a/imgui.h +++ b/imgui.h @@ -640,6 +640,7 @@ enum ImGuiNavInput_ ImGuiNavInput_PadFocusNext, // prev window (with PadMenu held) // e.g. R-trigger ImGuiNavInput_PadTweakSlow, // slower tweaks // e.g. L-trigger, analog ImGuiNavInput_PadTweakFast, // faster tweaks // e.g. R-trigger, analog + ImGuiNavInput_KeyMenu, // access menu // e.g. ALT ImGuiNavInput_COUNT, }; From 6f0aa766e12160d5ae3ad2238a4c4f8cc37c0823 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 23 Oct 2017 11:26:30 +0200 Subject: [PATCH 206/319] Nav: Added internal ImGuiNavReadMode_Released test for key releases. --- imgui.cpp | 5 ++++- imgui.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8edb6915..92926ef5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2611,6 +2611,7 @@ enum ImGuiNavReadMode { ImGuiNavReadMode_Down, ImGuiNavReadMode_Pressed, + ImGuiNavReadMode_Released, ImGuiNavReadMode_Repeat, ImGuiNavReadMode_RepeatSlow, ImGuiNavReadMode_RepeatFast @@ -2625,6 +2626,8 @@ static float GetNavInputAmount(ImGuiNavInput n, ImGuiNavReadMode mode) const float t = g.IO.NavInputsDownDuration[n]; // Duration pressed if (mode == ImGuiNavReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input (we don't need it for Pressed logic) return (t == 0.0f) ? 1.0f : 0.0f; + if (mode == ImGuiNavReadMode_Released) // Return 1.0f when just release, no repeat, ignore analog input (we don't need it for Pressed logic) + return (t < 0.0f && g.IO.NavInputsDownDurationPrev[n] >= 0.0f) ? 1.0f : 0.0f; if (mode == ImGuiNavReadMode_Repeat) return (float)ImGui::CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f); if (mode == ImGuiNavReadMode_RepeatSlow) @@ -3047,7 +3050,7 @@ void ImGui::NewFrame() memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f; - memcpy(g.IO.NavInputsPrev, g.IO.NavInputs, sizeof(g.IO.NavInputs)); + memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f; diff --git a/imgui.h b/imgui.h index 76d90e70..c31fd1ff 100644 --- a/imgui.h +++ b/imgui.h @@ -924,7 +924,7 @@ struct ImGuiIO float KeysDownDuration[512]; // Duration the keyboard key has been down (0.0f == just pressed) float KeysDownDurationPrev[512]; // Previous duration the key has been down float NavInputsDownDuration[ImGuiNavInput_COUNT]; - float NavInputsPrev[ImGuiNavInput_COUNT]; + float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; IMGUI_API ImGuiIO(); }; From be12f8c55d1e83c8ae2cf8cb69dda750503abbc2 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 23 Oct 2017 11:26:52 +0200 Subject: [PATCH 207/319] Nav: Fixed handling of ImGuiNavInput_KeyMenu so it doesn't set g.NavWindowingTarget at all, reducing size effects + handling menu layer toggle on Alt Release (#787) --- imgui.cpp | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 92926ef5..2a7ce38c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2664,11 +2664,13 @@ static ImVec2 GetNavInputAmount2d(int stick_no, ImGuiNavReadMode mode, float slo return delta; } -// Window management mode (change focus, move/resize window, jump back and forth to menu layer) +// Window management mode (change focus, move/resize window, toggle menu layer) static void ImGui::NavUpdateWindowing() { ImGuiContext& g = *GImGui; - if (!g.NavWindowingTarget && (IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiNavReadMode_Pressed) || IsNavInputPressed(ImGuiNavInput_KeyMenu, ImGuiNavReadMode_Pressed))) + bool toggle_layer = false; + + if (!g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiNavReadMode_Pressed)) { ImGuiWindow* window = g.NavWindow; if (!window) @@ -2713,7 +2715,7 @@ static void ImGui::NavUpdateWindowing() } } - if (!IsNavInputDown(ImGuiNavInput_PadMenu) && !IsNavInputDown(ImGuiNavInput_KeyMenu)) + if (!IsNavInputDown(ImGuiNavInput_PadMenu)) { // Apply actual focus only when releasing the NavMenu button (until then the window was merely rendered front-most) if (g.NavWindowingTarget && !g.NavWindowingToggleLayer && (!g.NavWindow || g.NavWindowingTarget != g.NavWindow->RootNonPopupWindow)) @@ -2727,20 +2729,27 @@ static void ImGui::NavUpdateWindowing() // Single press toggles NavLayer if (g.NavWindowingToggleLayer && g.NavWindow) - { - if ((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) == 0 && (g.NavWindow->RootWindow->DC.NavLayerActiveMask & (1 << 1)) != 0) - FocusWindow(g.NavWindow->RootWindow); - g.NavLayer = (g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0; - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - if (g.NavLayer == 0 && g.NavWindow->NavLastIds[0] != 0) - SetNavIDAndMoveMouse(g.NavWindow->NavLastIds[0], g.NavLayer, ImRect()); - else - NavInitWindow(g.NavWindow, true); - } + toggle_layer = true; g.NavWindowingTarget = NULL; } } + + // Keyboard: Press and release ALT to toggle menu + if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu, ImGuiNavReadMode_Released)) + toggle_layer = true; + + if (toggle_layer && g.NavWindow) + { + if ((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) == 0 && (g.NavWindow->RootWindow->DC.NavLayerActiveMask & (1 << 1)) != 0) + FocusWindow(g.NavWindow->RootWindow); + g.NavLayer = (g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0; + g.NavDisableHighlight = false; + g.NavDisableMouseHover = true; + if (g.NavLayer == 0 && g.NavWindow->NavLastIds[0] != 0) + SetNavIDAndMoveMouse(g.NavWindow->NavLastIds[0], g.NavLayer, ImRect()); + else + NavInitWindow(g.NavWindow, true); + } } // NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated. From 2ef2c104a166f12d1a666d8f52b202d126ffdcf7 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 23 Oct 2017 11:36:43 +0200 Subject: [PATCH 208/319] Begin: Minor tweaks --- imgui.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2a7ce38c..4a3cb4ff 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5332,11 +5332,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Draw window + handle manual resize ImRect title_bar_rect = window->TitleBarRect(); const float window_rounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding; + const bool window_is_focused = g.NavWindow && window->RootNonPopupWindow == g.NavWindow->RootNonPopupWindow; if (window->Collapsed) { // Title bar only - const bool is_focused = g.NavWindow && window->RootNonPopupWindow == g.NavWindow->RootNonPopupWindow && !g.NavDisableHighlight; - RenderFrame(title_bar_rect.Min, title_bar_rect.Max, GetColorU32(is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed), true, window_rounding); + RenderFrame(title_bar_rect.Min, title_bar_rect.Max, GetColorU32((window_is_focused && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed), true, window_rounding); } else { @@ -5409,9 +5409,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImGuiCorner_All : ImGuiCorner_BotLeft|ImGuiCorner_BotRight); // Title bar - const bool is_focused = g.NavWindow && window->RootNonPopupWindow == g.NavWindow->RootNonPopupWindow; if (!(flags & ImGuiWindowFlags_NoTitleBar)) - window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32(is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, ImGuiCorner_TopLeft|ImGuiCorner_TopRight); + window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, ImGuiCorner_TopLeft|ImGuiCorner_TopRight); // Menu bar if (flags & ImGuiWindowFlags_MenuBar) From 20983773f1f3b71ece6b04890ad2ec7f803668e0 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 23 Oct 2017 12:38:39 +0200 Subject: [PATCH 209/319] Nav: MainMenuBar now releases focus when user gets out of the menu layer. WindowingTarget when applying focus to a window with only menus automatically sets the layer. (#787) This is enough for basic mouse/gamepad usage, but 1- previous window gets an unfocused title bar color temporarily, 2- generaly for gamepad and especially keyboard we need much more to get this done right --- imgui.cpp | 14 ++++++++++++-- imgui_internal.h | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d8d0ae4c..fd7d9447 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2726,6 +2726,10 @@ static void ImGui::NavUpdateWindowing() g.NavDisableMouseHover = true; if (g.NavWindowingTarget->NavLastIds[0] == 0) NavInitWindow(g.NavWindowingTarget, false); + + // If the window only has a menu layer, select it directly + if (g.NavWindowingTarget->DC.NavLayerActiveMask == 0x02) + g.NavLayer = 1; } // Single press toggles NavLayer @@ -4077,7 +4081,7 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs) { ImGuiContext& g = *GImGui; - for (int i = g.Windows.Size-1; i >= 0; i--) + for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* window = g.Windows[i]; if (!window->Active) @@ -5818,7 +5822,7 @@ void ImGui::FocusPreviousWindow() { ImGuiContext& g = *GImGui; for (int i = g.Windows.Size - 1; i >= 0; i--) - if (g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow)) + if (g.Windows[i] != g.NavWindow && g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow)) { FocusWindow(g.Windows[i]); return; @@ -10293,6 +10297,12 @@ bool ImGui::BeginMainMenuBar() void ImGui::EndMainMenuBar() { EndMenuBar(); + + // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window + ImGuiContext& g = *GImGui; + if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0) + FocusPreviousWindow(); + End(); PopStyleVar(2); } diff --git a/imgui_internal.h b/imgui_internal.h index 25fb04a6..70c2a19c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -467,7 +467,7 @@ struct ImGuiContext ImVector CurrentPopupStack; // Which level of BeginPopup() we are in (reset every frame) // Navigation data (for gamepad/keyboard) - ImGuiWindow* NavWindow; // Focused window for navigation + ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' ImGuiID NavId; // Focused item for navigation ImGuiID NavActivateId; // ~~ IsNavInputPressed(ImGuiNavInput_PadActivate) ? NavId : 0, also set when calling ActivateItem() ImGuiID NavActivateDownId; // ~~ IsNavInputPressed(ImGuiNavInput_PadActivate) ? NavId : 0 From f6ff373b221b4bff34d03905d1aaca67dfa05a22 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 23 Oct 2017 14:54:23 +0200 Subject: [PATCH 210/319] Nav: #define IMGUI_HAS_NAV to ease sharing code across branches of imgui --- imgui.h | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.h b/imgui.h index c31fd1ff..245a0636 100644 --- a/imgui.h +++ b/imgui.h @@ -17,6 +17,7 @@ #include // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp #define IMGUI_VERSION "1.52 WIP" +#define IMGUI_HAS_NAV // navigation branch // Define attributes of all API symbols declarations, e.g. for DLL under Windows. #ifndef IMGUI_API From d55b69ad6efdfebd627b4daaffe15335c4570380 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 27 Oct 2017 20:06:45 +0200 Subject: [PATCH 211/319] Styles: Nav tweaks. --- imgui_draw.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index aeb7a284..48319ea3 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -220,6 +220,8 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); + colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); + colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.12f); } //----------------------------------------------------------------------------- From b9ebb34a047c6bdfe8ccd65f764f25fc874f9cbe Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 27 Oct 2017 20:54:48 +0200 Subject: [PATCH 212/319] Nav: ColorButton: Fixed a case of not using local 'hovered' flag directly, messes with some of my drag and drop work, and I can't find/understand the reason why this was left as is (there _was_ a reason at the time but it appears to be obsolete now?) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 2caee527..15db9ba6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10613,7 +10613,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl else window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border - if (!(flags & ImGuiColorEditFlags_NoTooltip) && IsItemHovered()) // FIXME-NAVIGATION: 'hovered' ? + if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); return pressed; From d323e8cca235ff0c04aa47662d6fa8fa9e69b890 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 19 Nov 2017 00:47:00 +0100 Subject: [PATCH 213/319] Fixed Style merge for nav branch (#707, #787) --- imgui_draw.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index ccd2ad7a..87e2b39f 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -133,19 +133,11 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_PopupBg] = ImVec4(0.11f, 0.11f, 0.14f, 0.92f); colors[ImGuiCol_Border] = ImVec4(0.70f, 0.70f, 0.70f, 0.27f); colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); -#if 1 - colors[ImGuiCol_FrameBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.30f); // Background of checkbox, radio button, plot, slider, text input - colors[ImGuiCol_FrameBgHovered] = ImVec4(0.90f, 0.80f, 0.80f, 0.40f); - colors[ImGuiCol_FrameBgActive] = ImVec4(0.90f, 0.65f, 0.65f, 0.45f); - colors[ImGuiCol_TitleBg] = ImVec4(0.24f, 0.24f, 0.50f, 0.83f); - colors[ImGuiCol_TitleBgActive] = ImVec4(0.33f, 0.33f, 0.65f, 0.87f); -#else colors[ImGuiCol_FrameBg] = ImVec4(0.43f, 0.43f, 0.43f, 0.39f); colors[ImGuiCol_FrameBgHovered] = ImVec4(0.47f, 0.47f, 0.69f, 0.40f); colors[ImGuiCol_FrameBgActive] = ImVec4(0.42f, 0.41f, 0.64f, 0.69f); colors[ImGuiCol_TitleBg] = ImVec4(0.27f, 0.27f, 0.54f, 0.83f); colors[ImGuiCol_TitleBgActive] = ImVec4(0.32f, 0.32f, 0.63f, 0.87f); -#endif colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.40f, 0.40f, 0.80f, 0.20f); colors[ImGuiCol_MenuBarBg] = ImVec4(0.40f, 0.40f, 0.55f, 0.80f); colors[ImGuiCol_ScrollbarBg] = ImVec4(0.20f, 0.25f, 0.30f, 0.60f); @@ -280,6 +272,8 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.45f, 0.00f, 1.00f); colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); + colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered]; + colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.40f, 0.40f, 0.40f, 0.12f); } From 7d09a0ae9948f99504ec12d0e297d08cffec0d85 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 22 Nov 2017 12:26:50 +0100 Subject: [PATCH 214/319] Menu bar: better software clipping to handle small windows, in particular child window don't have the minimum constraint added in e9a7e73bbaacec886f9b39130428b81b7f95bf16 so we need to render clipped menus better. --- imgui.cpp | 23 ++++++++++++++--------- imgui_internal.h | 1 + 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2323dce0..c1361f73 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4460,16 +4460,17 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); // Title bar - const bool is_focused = g.NavWindow && window->RootNonPopupWindow == g.NavWindow->RootNonPopupWindow; + const bool window_is_focused = g.NavWindow && window->RootNonPopupWindow == g.NavWindow->RootNonPopupWindow; if (!(flags & ImGuiWindowFlags_NoTitleBar)) - window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32(is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, ImDrawCornerFlags_Top); + window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, ImDrawCornerFlags_Top); // Menu bar if (flags & ImGuiWindowFlags_MenuBar) { ImRect menu_bar_rect = window->MenuBarRect(); - window->DrawList->AddRectFilled(menu_bar_rect.GetTL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top); - if (style.FrameBorderSize > 0.0f) + menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them. + window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top); + if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y) window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); } @@ -9231,14 +9232,18 @@ bool ImGui::BeginMenuBar() if (!(window->Flags & ImGuiWindowFlags_MenuBar)) return false; - ImGuiContext& g = *GImGui; IM_ASSERT(!window->DC.MenuBarAppending); BeginGroup(); // Save position PushID("##menubar"); - ImRect rect = window->MenuBarRect(); - rect.Max.x = ImMax(rect.Min.x, rect.Max.x - g.Style.WindowRounding); - PushClipRect(ImVec2(ImFloor(rect.Min.x+0.5f), ImFloor(rect.Min.y + window->WindowBorderSize + 0.5f)), ImVec2(ImFloor(rect.Max.x+0.5f), ImFloor(rect.Max.y+0.5f)), false); - window->DC.CursorPos = ImVec2(rect.Min.x + window->DC.MenuBarOffsetX, rect.Min.y);// + g.Style.FramePadding.y); + + // We don't clip with regular window clipping rectangle as it is already set to the area below. However we clip with window full rect. + // We remove 1 worth of rounding to Max.x to that text in long menus don't tend to display over the lower-right rounded area, which looks particularly glitchy. + ImRect bar_rect = window->MenuBarRect(); + ImRect clip_rect(ImFloor(bar_rect.Min.x + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - window->WindowRounding) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f)); + clip_rect.ClipWith(window->Rect()); + PushClipRect(clip_rect.Min, clip_rect.Max, false); + + window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffsetX, bar_rect.Min.y);// + g.Style.FramePadding.y); window->DC.LayoutType = ImGuiLayoutType_Horizontal; window->DC.MenuBarAppending = true; AlignTextToFramePadding(); diff --git a/imgui_internal.h b/imgui_internal.h index e54936a5..f155abf7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -746,6 +746,7 @@ public: ImGuiID GetID(const void* ptr); ImGuiID GetIDNoKeepAlive(const char* str, const char* str_end = NULL); + // We don't use g.FontSize because the window may be != g.CurrentWidow. ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x+Size.x, Pos.y+Size.y); } float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale; } float TitleBarHeight() const { return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f; } From 4e3c7f1557af3627f3882ab27bbaecd7cc559427 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 24 Nov 2017 16:50:11 +0100 Subject: [PATCH 215/319] Nav: Exposed NavInitWindow() in imgui_internal (#787) --- imgui.cpp | 4 ++-- imgui_internal.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 815858f2..53582450 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2528,7 +2528,7 @@ int ImGui::GetFrameCount() } // This needs to be called before we submit any widget (aka in or before Begin) -static void NavInitWindow(ImGuiWindow* window, bool force_reinit) +void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) { ImGuiContext& g = *GImGui; IM_ASSERT(window == g.NavWindow); @@ -4724,7 +4724,7 @@ static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b if (/*!(flags & ImGuiWindowFlags_NavFlattened) &&*/ (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll) && g.NavActivateId == id) { ImGui::FocusWindow(child_window); - NavInitWindow(child_window, false); + ImGui::NavInitWindow(child_window, false); ImGui::SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item g.ActiveIdSource = ImGuiInputSource_Nav; } diff --git a/imgui_internal.h b/imgui_internal.h index 5b238a35..0bae4d9b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -914,6 +914,8 @@ namespace ImGui IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip = true); + IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); + IMGUI_API int CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate); IMGUI_API void Scrollbar(ImGuiLayoutType direction); From 46e994de4e9b334e5bf56a9f6f33bf618c72cb9a Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 24 Nov 2017 16:54:03 +0100 Subject: [PATCH 216/319] Nav: Do not clear last navigation id stored in a hidden child window. (#787) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 53582450..c5a804d8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5054,7 +5054,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames == 1); - if (window_just_appearing_after_hidden_for_resize) + if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow)) window->NavLastIds[0] = 0; window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize); window->CloseButton = (p_open != NULL); From 2ca4f9e862fcd4076fb5693b9cc343c92e2564ea Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 5 Dec 2017 20:36:35 +0100 Subject: [PATCH 217/319] Added ImGuiWindowFlags_ResizeFromAnySide flag and code to resize from any of the 4 corners (only 2 corners enabled). (#822) --- imgui.cpp | 115 +++++++++++++++++++++++++++++++++++++++--------------- imgui.h | 2 + 2 files changed, 86 insertions(+), 31 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e6a91bc6..1eaf0efa 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4198,6 +4198,37 @@ static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags) return ImGuiCol_WindowBg; } +static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size) +{ + ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left + ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right + ImVec2 size_expected = pos_max - pos_min; + ImVec2 size_constrained = CalcSizeFullWithConstraint(window, size_expected); + *out_pos = pos_min; + if (corner_norm.x == 0.0f) + out_pos->x -= (size_constrained.x - size_expected.x); + if (corner_norm.y == 0.0f) + out_pos->y -= (size_constrained.y - size_expected.y); + *out_size = size_constrained; +} + +struct ImGuiResizeGripDef +{ + const char* StrId; + ImVec2 CornerNorm; + ImVec2 InnerDir; + int AngleMin12, AngleMax12; + ImGuiMouseCursor MouseCursor; +}; + +const ImGuiResizeGripDef resize_grip_def[4] = +{ + { "#RESIZE0", ImVec2(1,1), ImVec2(-1,-1), 0, 3, ImGuiMouseCursor_ResizeNWSE }, // Lower right + { "#RESIZE1", ImVec2(0,1), ImVec2(+1,-1), 3, 6, ImGuiMouseCursor_ResizeNESW }, // Lower left + { "#RESIZE2", ImVec2(0,0), ImVec2(+1,+1), 6, 9, ImGuiMouseCursor_ResizeNWSE }, // Upper left + { "#RESIZE3", ImVec2(1,0), ImVec2(-1,+1), 9,12, ImGuiMouseCursor_ResizeNESW }, // Upper right +}; + // Push a new ImGui window to add widgets to. // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. // - Begin/End can be called multiple times during the frame with the same window name to append content. @@ -4533,39 +4564,58 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } else { - ImU32 resize_col = 0; - const float resize_corner_size = ImMax(g.FontSize * 1.35f, window_rounding + 1.0f + g.FontSize * 0.2f); + // Handle resize for: Resize Grips, Gamepad + ImU32 resize_grip_col[4] = { 0 }; + const int resize_grip_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 2 : 1; // 4 + const float resize_corner_size = (float)(int)ImMax(g.FontSize * 1.35f, window_rounding + 1.0f + g.FontSize * 0.2f); if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && !(flags & ImGuiWindowFlags_NoResize)) { - // Manual resize - // Using the FlattenChilds button flag, we make the resize button accessible even if we are hovering over a child window - const ImVec2 br = window->Rect().GetBR(); - const ImRect resize_rect(br - ImFloor(ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f)), br); - const ImGuiID resize_id = window->GetID("#RESIZE"); - bool hovered, held; - ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds); - resize_col = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); - if (hovered || held) - g.MouseCursor = ImGuiMouseCursor_ResizeNWSE; + ImVec2 pos_target(FLT_MAX, FLT_MAX); + ImVec2 size_target(FLT_MAX, FLT_MAX); - ImVec2 size_target(FLT_MAX,FLT_MAX); - if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0]) + // Manual resize grips + for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) { - // Manual auto-fit when double-clicking - size_target = size_auto_fit; - ClearActiveID(); - } - else if (held) - { - // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - size_target = (g.IO.MousePos - g.ActiveIdClickOffset - window->Pos) + resize_rect.GetSize(); + const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; + const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerNorm); + + // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window + ImRect resize_rect; + resize_rect.Add(corner); + resize_rect.Add(corner + grip.InnerDir * (float)(int)(resize_corner_size * 0.75f)); + bool hovered, held; + ButtonBehavior(resize_rect, window->GetID(grip.StrId), &hovered, &held, ImGuiButtonFlags_FlattenChilds); + if (hovered || held) + g.MouseCursor = grip.MouseCursor; + + if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0]) + { + // Manual auto-fit when double-clicking + size_target = CalcSizeFullWithConstraint(window, size_auto_fit); + ClearActiveID(); + } + else if (held) + { + // Resize from any of the four corners + // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position + ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerNorm; // Corner of the window corresponding to our corner grip + CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerNorm, &pos_target, &size_target); + } + resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); } - if (size_target.x != FLT_MAX && size_target.y != FLT_MAX) + // Apply back modified position/size to window + if (size_target.x != FLT_MAX) { - window->SizeFull = CalcSizeFullWithConstraint(window, size_target); + window->SizeFull = size_target; MarkIniSettingsDirty(window); } + if (pos_target.x != FLT_MAX) + { + window->Pos = window->PosFloat = ImVec2((float)(int)pos_target.x, (float)(int)pos_target.y); + MarkIniSettingsDirty(window); + } + window->Size = window->SizeFull; title_bar_rect = window->TitleBarRect(); } @@ -4595,15 +4645,18 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->ScrollbarY) Scrollbar(ImGuiLayoutType_Vertical); - // Render resize grip - // (after the input handling so we don't have a frame of latency) + // Render resize grips (after their input handling so we don't have a frame of latency) if (!(flags & ImGuiWindowFlags_NoResize)) { - const ImVec2 br = window->Rect().GetBR(); - window->DrawList->PathLineTo(br + ImVec2(-resize_corner_size, -window_border_size)); - window->DrawList->PathLineTo(br + ImVec2(-window_border_size, -resize_corner_size)); - window->DrawList->PathArcToFast(ImVec2(br.x - window_rounding - window_border_size, br.y - window_rounding - window_border_size), window_rounding, 0, 3); - window->DrawList->PathFillConvex(resize_col); + for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) + { + const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; + const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerNorm); + window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_corner_size) : ImVec2(resize_corner_size, window_border_size))); + window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_corner_size, window_border_size) : ImVec2(window_border_size, resize_corner_size))); + window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); + window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); + } } // Borders diff --git a/imgui.h b/imgui.h index f0920910..ec5692e1 100644 --- a/imgui.h +++ b/imgui.h @@ -513,6 +513,8 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14, // Always show vertical scrollbar (even if ContentSize.y < Size.y) ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, // Always show horizontal scrollbar (even if ContentSize.x < Size.x) ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) + ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, + // [Internal] ImGuiWindowFlags_ChildWindow = 1 << 22, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_ComboBox = 1 << 23, // Don't use! For internal use by ComboBox() From b9dc0caee339e8b661e7ebf511138f68bfb0ee43 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 5 Dec 2017 22:05:24 +0100 Subject: [PATCH 218/319] Tweak four-corners resize grip code. Added ImRect::FixInverted() helper. (#822) --- imgui.cpp | 39 ++++++++++++++++++++------------------- imgui_internal.h | 1 + 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1eaf0efa..5d95827b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4214,19 +4214,17 @@ static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& co struct ImGuiResizeGripDef { - const char* StrId; - ImVec2 CornerNorm; + ImVec2 CornerPos; ImVec2 InnerDir; int AngleMin12, AngleMax12; - ImGuiMouseCursor MouseCursor; }; const ImGuiResizeGripDef resize_grip_def[4] = { - { "#RESIZE0", ImVec2(1,1), ImVec2(-1,-1), 0, 3, ImGuiMouseCursor_ResizeNWSE }, // Lower right - { "#RESIZE1", ImVec2(0,1), ImVec2(+1,-1), 3, 6, ImGuiMouseCursor_ResizeNESW }, // Lower left - { "#RESIZE2", ImVec2(0,0), ImVec2(+1,+1), 6, 9, ImGuiMouseCursor_ResizeNWSE }, // Upper left - { "#RESIZE3", ImVec2(1,0), ImVec2(-1,+1), 9,12, ImGuiMouseCursor_ResizeNESW }, // Upper right + { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right + { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left + { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left + { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right }; // Push a new ImGui window to add widgets to. @@ -4567,26 +4565,28 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Handle resize for: Resize Grips, Gamepad ImU32 resize_grip_col[4] = { 0 }; const int resize_grip_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 2 : 1; // 4 - const float resize_corner_size = (float)(int)ImMax(g.FontSize * 1.35f, window_rounding + 1.0f + g.FontSize * 0.2f); + + const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window_rounding + 1.0f + g.FontSize * 0.2f); + const float grip_hover_size = (float)(int)(grip_draw_size * 0.75f); if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && !(flags & ImGuiWindowFlags_NoResize)) { ImVec2 pos_target(FLT_MAX, FLT_MAX); ImVec2 size_target(FLT_MAX, FLT_MAX); // Manual resize grips + PushID("#RESIZE"); for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) { const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; - const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerNorm); + const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos); // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window - ImRect resize_rect; - resize_rect.Add(corner); - resize_rect.Add(corner + grip.InnerDir * (float)(int)(resize_corner_size * 0.75f)); + ImRect resize_rect(corner, corner + grip.InnerDir * grip_hover_size); + resize_rect.FixInverted(); bool hovered, held; - ButtonBehavior(resize_rect, window->GetID(grip.StrId), &hovered, &held, ImGuiButtonFlags_FlattenChilds); + ButtonBehavior(resize_rect, window->GetID((void*)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChilds); if (hovered || held) - g.MouseCursor = grip.MouseCursor; + g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0]) { @@ -4598,11 +4598,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Resize from any of the four corners // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerNorm; // Corner of the window corresponding to our corner grip - CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerNorm, &pos_target, &size_target); + ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos; // Corner of the window corresponding to our corner grip + CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target); } resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); } + PopID(); // Apply back modified position/size to window if (size_target.x != FLT_MAX) @@ -4651,9 +4652,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) { const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; - const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerNorm); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_corner_size) : ImVec2(resize_corner_size, window_border_size))); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_corner_size, window_border_size) : ImVec2(window_border_size, resize_corner_size))); + const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos); + window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size))); + window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size))); window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); } diff --git a/imgui_internal.h b/imgui_internal.h index b3371f5c..23992c43 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -281,6 +281,7 @@ struct IMGUI_API ImRect void Translate(const ImVec2& v) { Min.x += v.x; Min.y += v.y; Max.x += v.x; Max.y += v.y; } void ClipWith(const ImRect& clip) { if (Min.x < clip.Min.x) Min.x = clip.Min.x; if (Min.y < clip.Min.y) Min.y = clip.Min.y; if (Max.x > clip.Max.x) Max.x = clip.Max.x; if (Max.y > clip.Max.y) Max.y = clip.Max.y; } void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; } + void FixInverted() { if (Min.x > Max.x) ImSwap(Min.x, Max.x); if (Min.y > Max.y) ImSwap(Min.y, Max.y); } bool IsFinite() const { return Min.x != FLT_MAX; } ImVec2 GetClosestPoint(ImVec2 p, bool on_edge) const { From 5686c72bbdb01c120e3eae4c8271c534c92c6c94 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 5 Dec 2017 22:38:02 +0100 Subject: [PATCH 219/319] Windows can be resized from their borders when ImGuiWindowFlags_ResizeFromAnySide is set. (#822) The interaction is currently unsatisfying because we can only reach a window from its inner rectangle (because of HoveredWindow filtering). --- imgui.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 5d95827b..c02e2920 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4227,6 +4227,18 @@ const ImGuiResizeGripDef resize_grip_def[4] = { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right }; +static ImRect GetBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) +{ + ImRect rect = window->Rect(); + if (thickness == 0.0f) rect.Max -= ImVec2(1,1); + if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y, rect.Max.x - perp_padding, rect.Min.y + thickness); + if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x, rect.Max.y - perp_padding); + if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y); + if (border_n == 3) return ImRect(rect.Min.x, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); + IM_ASSERT(0); + return ImRect(); +} + // Push a new ImGui window to add widgets to. // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. // - Begin/End can be called multiple times during the frame with the same window name to append content. @@ -4562,9 +4574,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } else { - // Handle resize for: Resize Grips, Gamepad + // Handle resize for: Resize Grips, Borders, Gamepad + int border_hovered = -1, border_held = -1; ImU32 resize_grip_col[4] = { 0 }; const int resize_grip_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 2 : 1; // 4 + const int resize_border_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 4 : 0; const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window_rounding + 1.0f + g.FontSize * 0.2f); const float grip_hover_size = (float)(int)(grip_draw_size * 0.75f); @@ -4603,6 +4617,30 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); } + for (int border_n = 0; border_n < resize_border_count; border_n++) + { + const float BORDER_SIZE = 5.0f; // FIXME: Only works _inside_ window because of HoveredWindow check. + const float BORDER_APPEAR_TIMER = 0.05f; // Reduce visual noise + bool hovered, held; + ImRect border_rect = GetBorderRect(window, border_n, grip_hover_size, BORDER_SIZE); + ButtonBehavior(border_rect, window->GetID((void*)(border_n+4)), &hovered, &held, ImGuiButtonFlags_FlattenChilds); + if ((hovered && g.HoveredIdTimer > BORDER_APPEAR_TIMER) || (held && g.ActiveIdTimer > BORDER_APPEAR_TIMER)) + { + g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; + if (hovered) border_hovered = border_n; + if (held) border_held = border_n; + } + if (held) + { + ImVec2 border_target = window->Pos; + ImVec2 border_posn; + if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y); } + if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + BORDER_SIZE); } + if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + BORDER_SIZE); } + if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x); } + CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target); + } + } PopID(); // Apply back modified position/size to window @@ -4663,6 +4701,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Borders if (window_border_size > 0.0f) window->DrawList->AddRect(window->Pos, window->Pos+window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size); + if (border_held != -1 || border_hovered != -1) + { + ImRect border = GetBorderRect(window, border_held != -1 ? border_held : border_hovered, grip_draw_size, 0.0f); + window->DrawList->AddLine(border.Min, border.Max, GetColorU32(border_held != -1 ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered), ImMax(1.0f, window_border_size)); + } if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar)) window->DrawList->AddLine(title_bar_rect.GetBL()+ImVec2(1,-1), title_bar_rect.GetBR()+ImVec2(-1,-1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); } From 0f119865a67cd5d92e89a2da49656d3a9e6c4873 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 5 Dec 2017 22:53:58 +0100 Subject: [PATCH 220/319] Comments about ImGuiWindowFlags_ResizeFromAnySide. Removed hovering color. May need its own color. (#822) --- imgui.cpp | 11 +++++------ imgui.h | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c02e2920..5c5a7f7b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4575,7 +4575,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else { // Handle resize for: Resize Grips, Borders, Gamepad - int border_hovered = -1, border_held = -1; + int border_held = -1; ImU32 resize_grip_col[4] = { 0 }; const int resize_grip_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 2 : 1; // 4 const int resize_border_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 4 : 0; @@ -4624,10 +4624,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) bool hovered, held; ImRect border_rect = GetBorderRect(window, border_n, grip_hover_size, BORDER_SIZE); ButtonBehavior(border_rect, window->GetID((void*)(border_n+4)), &hovered, &held, ImGuiButtonFlags_FlattenChilds); - if ((hovered && g.HoveredIdTimer > BORDER_APPEAR_TIMER) || (held && g.ActiveIdTimer > BORDER_APPEAR_TIMER)) + if ((hovered && g.HoveredIdTimer > BORDER_APPEAR_TIMER) || held) { g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; - if (hovered) border_hovered = border_n; if (held) border_held = border_n; } if (held) @@ -4701,10 +4700,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Borders if (window_border_size > 0.0f) window->DrawList->AddRect(window->Pos, window->Pos+window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size); - if (border_held != -1 || border_hovered != -1) + if (border_held != -1) { - ImRect border = GetBorderRect(window, border_held != -1 ? border_held : border_hovered, grip_draw_size, 0.0f); - window->DrawList->AddLine(border.Min, border.Max, GetColorU32(border_held != -1 ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered), ImMax(1.0f, window_border_size)); + ImRect border = GetBorderRect(window, border_held, grip_draw_size, 0.0f); + window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size)); } if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar)) window->DrawList->AddLine(title_bar_rect.GetBL()+ImVec2(1,-1), title_bar_rect.GetBR()+ImVec2(-1,-1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); diff --git a/imgui.h b/imgui.h index ec5692e1..71fe24c2 100644 --- a/imgui.h +++ b/imgui.h @@ -513,7 +513,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14, // Always show vertical scrollbar (even if ContentSize.y < Size.y) ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, // Always show horizontal scrollbar (even if ContentSize.x < Size.x) ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) - ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, + ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // (WIP) Enable resize from any corners and borders. Your back-end needs to honor the different values of io.MouseCursor set by imgui. // [Internal] ImGuiWindowFlags_ChildWindow = 1 << 22, // Don't use! For internal use by BeginChild() From 2fc9a2e6e7f36a2b36dc0359ea3316558795c22f Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 6 Dec 2017 15:20:36 +0100 Subject: [PATCH 221/319] Fixed nav branch merge issue. --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index a99c3743..a84cd970 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5520,7 +5520,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) ImRect resize_rect(corner, corner + grip.InnerDir * grip_hover_size); resize_rect.FixInverted(); bool hovered, held; - ButtonBehavior(resize_rect, window->GetID((void*)resize_grip_n), &hovered, &held, ImGuiButtonFlags_NoNavFocus); + ButtonBehavior(resize_rect, window->GetID((void*)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChilds|ImGuiButtonFlags_NoNavFocus); if (hovered || held) g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; From 6deb865f78c377004aae03658c88e0d8dee89f04 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 6 Dec 2017 17:48:28 +0100 Subject: [PATCH 222/319] Nav: Merge fix. --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 73e77f76..4b487a2a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5521,7 +5521,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) ImRect resize_rect(corner, corner + grip.InnerDir * grip_hover_size); resize_rect.FixInverted(); bool hovered, held; - ButtonBehavior(resize_rect, window->GetID((void*)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChilds); + ButtonBehavior(resize_rect, window->GetID((void*)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChilds | ImGuiButtonFlags_NoNavFocus); if (hovered || held) g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; @@ -5547,7 +5547,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const float BORDER_APPEAR_TIMER = 0.05f; // Reduce visual noise bool hovered, held; ImRect border_rect = GetBorderRect(window, border_n, grip_hover_size, BORDER_SIZE); - ButtonBehavior(border_rect, window->GetID((void*)(border_n+4)), &hovered, &held, ImGuiButtonFlags_FlattenChilds); + ButtonBehavior(border_rect, window->GetID((void*)(border_n+4)), &hovered, &held, ImGuiButtonFlags_FlattenChilds | ImGuiButtonFlags_NoNavFocus); if ((hovered && g.HoveredIdTimer > BORDER_APPEAR_TIMER) || held) { g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; From 9ce51ad9f6c39a3b21799e39991484debf95fb5a Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 8 Dec 2017 15:11:35 +0100 Subject: [PATCH 223/319] Fix bad merge --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 5d6e5df6..f30884ab 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5633,7 +5633,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // FIXME-NAVIGATION: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck. g.NavWindowingToggleLayer = false; - size_target = CalcSizeFullWithConstraint(window, window->SizeFull + nav_resize_delta); + size_target = CalcSizeAfterConstraint(window, window->SizeFull + nav_resize_delta); } } } From 90d0b8b58b7957b6e73fde769d00ac110ff6bebe Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 12 Dec 2017 23:41:20 +0100 Subject: [PATCH 224/319] Navigation: minor sync to reduce drifts between changes --- imgui.cpp | 8 +++++--- imgui_internal.h | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 22202004..54f5e229 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4794,8 +4794,9 @@ bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button) ImGuiWindow* window = GImGui->CurrentWindow; ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! IM_ASSERT(id != 0); // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) - if (IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - OpenPopupEx(id, true); + if (IsMouseClicked(mouse_button)) + if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) + OpenPopupEx(id, true); return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize); } @@ -11525,9 +11526,10 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl value_changed = value_changed_h = true; } } + + // Alpha bar logic if (alpha_bar) { - // Alpha bar logic SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y)); InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size)); if (IsItemActive()) diff --git a/imgui_internal.h b/imgui_internal.h index b6afde12..f85e4bb5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -182,7 +182,7 @@ enum ImGuiButtonFlags_ ImGuiButtonFlags_PressedOnRelease = 1 << 3, // return true on release (default requires click+release) ImGuiButtonFlags_PressedOnDoubleClick = 1 << 4, // return true on double-click (default requires click+release) ImGuiButtonFlags_FlattenChildren = 1 << 5, // allow interactions even if a child window is overlapping - ImGuiButtonFlags_AllowItemOverlap = 1 << 6, // require previous frame HoveredId to either match id or be null before being usable + ImGuiButtonFlags_AllowItemOverlap = 1 << 6, // require previous frame HoveredId to either match id or be null before being usable, use along with SetItemAllowOverlap() ImGuiButtonFlags_DontClosePopups = 1 << 7, // disable automatically closing parent popup on press // [UNUSED] ImGuiButtonFlags_Disabled = 1 << 8, // disable interactions ImGuiButtonFlags_AlignTextBaseLine = 1 << 9, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine From 6c63c7a8b16fdabbb79f7e7f9c75b9a25f17c631 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 23 Dec 2017 16:40:12 +0100 Subject: [PATCH 225/319] Various zealous warning fixes (thanks Clang). (Navigation branch) --- imgui.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f48eaa91..6db14ae6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2149,7 +2149,7 @@ static bool NavScoreItem(ImRect cand) // FIXME-NAVIGATION: Introducing biases for vertical navigation, needs to be removed. float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items - if (dby && dbx) + if (dby != 0.0f && dbx != 0.0f) dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f); float dist_box = fabsf(dbx) + fabsf(dby); @@ -2161,7 +2161,7 @@ static bool NavScoreItem(ImRect cand) // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance ImGuiDir quadrant; float dax = 0.0f, day = 0.0f, dist_axial = 0.0f; - if (dbx || dby) + if (dbx != 0.0f || dby != 0.0f) { // For non-overlapping boxes, use distance between boxes dax = dbx; @@ -2169,7 +2169,7 @@ static bool NavScoreItem(ImRect cand) dist_axial = dist_box; quadrant = NavScoreItemGetQuadrant(dbx, dby); } - else if (dcx || dcy) + else if (dcx != 0.0f || dcy != 0.0f) { // For overlapping boxes with different centers, use distance between centers dax = dcx; @@ -2232,7 +2232,10 @@ static bool NavScoreItem(ImRect cand) if (g.NavMoveResultDistBox == FLT_MAX && dist_axial < g.NavMoveResultDistAxial) // Check axial match if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f)) - g.NavMoveResultDistAxial = dist_axial, new_best = true; + { + g.NavMoveResultDistAxial = dist_axial; + new_best = true; + } return new_best; } @@ -8329,7 +8332,8 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivateDownId == id) { const ImVec2 delta2 = GetNavInputAmount2d(0, ImGuiNavReadMode_RepeatFast, 0.0f, 0.0f); - if (float delta = is_horizontal ? delta2.x : -delta2.y) + float delta = is_horizontal ? delta2.x : -delta2.y; + if (delta != 0.0f) { clicked_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos); if (decimal_precision == 0 && !is_non_linear) From 03f5cd6ca128090f71df7959e0f92c6b40076184 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 9 Jan 2018 00:05:04 +0100 Subject: [PATCH 226/319] Nav: Fixed RenderNavHighlight() clipping, essentially revert 6ea90af6b76f2b9f7b86c12813bafd6f0408bdc0. (#787) --- imgui.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index bd4ffb00..6b868670 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4147,7 +4147,6 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl ImGuiWindow* window = ImGui::GetCurrentWindow(); ImRect display_rect = bb; - display_rect.ClipWith(window->ClipRect); if (flags & ImGuiNavHighlightFlags_TypeDefault) { const float THICKNESS = 2.0f; From 0a982027140c01121cf214c18efcd4f5444d364e Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 12 Jan 2018 19:13:15 +0100 Subject: [PATCH 227/319] Nav: Standardized FIXME-NAV marker. --- imgui.cpp | 12 ++++++------ imgui_internal.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9746251d..f09ff856 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2,7 +2,7 @@ // (main code and documentation) // ** EXPERIMENTAL GAMEPAD/KEYBOARD NAVIGATION BRANCH -// ** Grep for FIXME-NAVIGATION +// ** Grep for FIXME-NAV // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. // Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase. @@ -2153,7 +2153,7 @@ static bool NavScoreItem(ImRect cand) } // Compute distance between boxes - // FIXME-NAVIGATION: Introducing biases for vertical navigation, needs to be removed. + // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed. float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items if (dby != 0.0f && dbx != 0.0f) @@ -2654,7 +2654,7 @@ enum ImGuiNavReadMode ImGuiNavReadMode_RepeatFast }; -// FIXME-NAVIGATION: Expose navigation repeat delay/rate +// FIXME-NAV: Expose navigation repeat delay/rate static float GetNavInputAmount(ImGuiNavInput n, ImGuiNavReadMode mode) { ImGuiContext& g = *GImGui; @@ -5320,7 +5320,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive); if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) { - // FIXME-NAVIGATION: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck. + // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck. g.NavWindowingToggleLayer = false; size_target = CalcSizeAfterConstraint(window, window->SizeFull + nav_resize_delta); } @@ -7278,7 +7278,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // PressedOnClick | | .. // PressedOnRelease | | .. (NOT on release) // PressedOnDoubleClick | | .. - // FIXME-NAVIGATION: We don't honor those different behaviors. + // FIXME-NAV: We don't honor those different behaviors. if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0]) { SetActiveID(id, window); @@ -8375,7 +8375,7 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v } if (IsNavInputDown(ImGuiNavInput_PadTweakFast)) delta *= 10.0f; - clicked_t = ImSaturate(clicked_t + delta); // FIXME-NAVIGATION: todo: cancel adjustment if current value already past edge and we are moving in edge direction, to avoid clamping value to edge. + clicked_t = ImSaturate(clicked_t + delta); // FIXME-NAV: todo: cancel adjustment if current value already past edge and we are moving in edge direction, to avoid clamping value to edge. set_new_value = true; } } diff --git a/imgui_internal.h b/imgui_internal.h index 816c56ac..17e26481 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -912,7 +912,7 @@ struct IMGUI_API ImGuiWindow ImGuiWindow* RootNavWindow; // Generally point to ourself. If we are a child window with the ImGuiWindowFlags_NavFlattenedChild flag, point to parent. Used to display TitleBgActive color and for selecting which window to use for NavWindowing. // Navigation / Focus - // FIXME-NAVIGATION: Merge all this with the new Nav system, at least the request variables should be moved to ImGuiContext + // FIXME-NAV: Merge all this with the new Nav system, at least the request variables should be moved to ImGuiContext int FocusIdxAllCounter; // Start at -1 and increase as assigned via FocusItemRegister() int FocusIdxTabCounter; // (same, but only count widgets which you can Tab through) int FocusIdxAllRequestCurrent; // Item being requested for focus From c85d7d6e4932f4529059dad3f1b148a66b4ca846 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 12 Jan 2018 19:28:37 +0100 Subject: [PATCH 228/319] Nav: Remove GetItemID(), hide ActivateItem() before this feature is unfinished and has issue (full feature is on hold). Undo part of 59c6f35bf6b8919000d7deacc0a7b1c1428a2519 (#787) --- imgui.cpp | 6 ------ imgui.h | 2 -- imgui_demo.cpp | 16 ---------------- imgui_internal.h | 1 + 4 files changed, 1 insertion(+), 24 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4e46158c..755d49f8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6965,12 +6965,6 @@ void ImGui::ActivateItem(ImGuiID id) g.NavNextActivateId = id; } -ImGuiID ImGui::GetItemID() -{ - ImGuiContext& g = *GImGui; - return g.CurrentWindow->DC.LastItemId; -} - void ImGui::SetKeyboardFocusHere(int offset) { IM_ASSERT(offset >= -1); // -1 is allowed but not below diff --git a/imgui.h b/imgui.h index 280a9f5e..e73a5445 100644 --- a/imgui.h +++ b/imgui.h @@ -448,8 +448,6 @@ namespace ImGui IMGUI_API void StyleColorsLight(ImGuiStyle* dst = NULL); // Focus, Activation - IMGUI_API void ActivateItem(ImGuiID id); // remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. - IMGUI_API ImGuiID GetItemID(); // get id of previous item, generally ~GetID(label) IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. Please use instead of "if (IsWindowAppearing()) SetScrollHere()" to signify "default item". IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d6be2649..67be3577 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1887,22 +1887,6 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::TreePop(); } -#if 0 - if (ImGui::TreeNode("Remote Activation")) - { - static char label[256]; - ImGui::InputText("Label", label, IM_ARRAYSIZE(label)); - ImGui::PopID(); // We don't yet have an easy way compute ID at other levels of the ID stack so we pop it manually for now (e.g. we'd like something like GetID("../label")) - ImGuiID id = ImGui::GetID(label); - ImGui::PushID("Remote Activation"); - if (ImGui::SmallButton("Activate")) - ImGui::ActivateItem(id); - ImGui::SameLine(); - ImGui::Text("ID = 0x%08X", id); - ImGui::TreePop(); - } -#endif - if (ImGui::TreeNode("Focused & Hovered Test")) { static bool embed_all_inside_a_child_window = false; diff --git a/imgui_internal.h b/imgui_internal.h index cc66758b..8f4ecc20 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1003,6 +1003,7 @@ namespace ImGui IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip = true); IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); + IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. IMGUI_API int CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate); From db63e71f13eac9fa9350baedaaef6552ecae7ed5 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 12 Jan 2018 20:07:01 +0100 Subject: [PATCH 229/319] Internals: Exposed SetCurrentFont() in imgui_internal.h --- imgui.cpp | 3 +-- imgui_internal.h | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 755d49f8..57b24157 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -674,7 +674,6 @@ static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true); static ImFont* GetDefaultFont(); -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); @@ -6220,7 +6219,7 @@ static ImFont* GetDefaultFont() return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } -static void SetCurrentFont(ImFont* font) +void ImGui::SetCurrentFont(ImFont* font) { ImGuiContext& g = *GImGui; IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? diff --git a/imgui_internal.h b/imgui_internal.h index 8f4ecc20..2d6ec52b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -996,6 +996,8 @@ namespace ImGui IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); IMGUI_API void PopItemFlag(); + IMGUI_API void SetCurrentFont(ImFont* font); + IMGUI_API void OpenPopupEx(ImGuiID id); IMGUI_API void ClosePopup(ImGuiID id); IMGUI_API bool IsPopupOpen(ImGuiID id); From 6eff21ee5e33bc6daab915020915fa8d94459812 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 15 Jan 2018 16:19:01 +0100 Subject: [PATCH 230/319] Revert "Nav: Fixed RenderNavHighlight() clipping, essentially revert 6ea90af6b76f2b9f7b86c12813bafd6f0408bdc0. (#787)" This reverts commit 03f5cd6ca128090f71df7959e0f92c6b40076184. --- imgui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.cpp b/imgui.cpp index 57b24157..368db256 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4159,6 +4159,7 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl ImGuiWindow* window = ImGui::GetCurrentWindow(); ImRect display_rect = bb; + display_rect.ClipWith(window->ClipRect); if (flags & ImGuiNavHighlightFlags_TypeDefault) { const float THICKNESS = 2.0f; From a221d253f3afcbd822f87e385c33906e388e8705 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 15 Jan 2018 19:09:40 +0100 Subject: [PATCH 231/319] Nav: Comment. --- imgui_internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_internal.h b/imgui_internal.h index 1f39a662..0c8f4088 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -583,7 +583,7 @@ struct ImGuiContext ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest) ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring. - ImGuiWindow* NavWindowingTarget; + ImGuiWindow* NavWindowingTarget; // When selecting a window (holding Menu+FocusPrev/Next, or equivalent of CTRL-TAB) this window is temporarily displayed front-most. float NavWindowingDisplayAlpha; bool NavWindowingToggleLayer; int NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. From 05885b2e6df95d7c044f60581ddcef4b7c59c831 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 15 Jan 2018 20:18:01 +0100 Subject: [PATCH 232/319] Merge fix. --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 300dc7a3..406f3a30 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3869,7 +3869,7 @@ void ImGui::Render() AddWindowToDrawDataSelectLayer(window); } if (g.NavWindowingTarget && g.NavWindowingTarget->Active && g.NavWindowingTarget->HiddenFrames <= 0) // NavWindowingTarget is always temporarily displayed as the front-most window - AddWindowToDrawDataSelectLayer(&g.DrawDataBuilder, g.NavWindowingTarget); + AddWindowToDrawDataSelectLayer(g.NavWindowingTarget); g.DrawDataBuilder.FlattenIntoSingleLayer(); // Draw software mouse cursor if requested From 2f15cc085599db9a6e4d799a45b015574d76fc78 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 15 Jan 2018 21:55:32 +0100 Subject: [PATCH 233/319] Nav: Fixed popup wrap-around logic for windows with scrolling. (#787) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 406f3a30..c8cf1366 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4790,7 +4790,7 @@ static void NavProcessMoveRequestWrapAround(ImGuiWindow* window) { g.NavMoveRequestForwardStep = 1; NavMoveRequestCancel(); - g.NavWindow->NavRectRel[0].Min.y = g.NavWindow->NavRectRel[0].Max.y = (g.NavMoveDir == ImGuiDir_Up) ? window->SizeFull.y : 0.0f; + g.NavWindow->NavRectRel[0].Min.y = g.NavWindow->NavRectRel[0].Max.y = ((g.NavMoveDir == ImGuiDir_Up) ? ImMax(window->SizeFull.y, window->SizeContents.y) : 0.0f) - window->Scroll.y; } } From 3ed2ddbfbe5c913fd5b3ff03c0f778e8e66747e0 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 21 Jan 2018 20:32:05 +0100 Subject: [PATCH 234/319] Metrics: Removed context size display. --- imgui.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 40780986..62f86342 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12810,7 +12810,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3); ImGui::Text("%d allocations", ImGui::GetIO().MetricsAllocs); - ImGui::Text("sizeof(ImGuiContext) = %u, sizeof(ImGuiWindow) = %u", (int)sizeof(ImGuiContext), (int)sizeof(ImGuiWindow)); static bool show_clip_rects = true; ImGui::Checkbox("Show clipping rectangles when hovering an ImDrawCmd", &show_clip_rects); ImGui::Separator(); From 13c407591ef340610a64b185c6b8aed80060ae20 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 23 Jan 2018 16:45:46 +0100 Subject: [PATCH 235/319] Nav: Comments, tweaks --- imgui.cpp | 6 +++--- imgui.h | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d2d81ef8..dd005063 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2020,10 +2020,10 @@ ImGuiID ImGui::GetActiveID() void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) { - IM_ASSERT(id != 0); ImGuiContext& g = *GImGui; - - // Assume that SetActiveID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it. + IM_ASSERT(id != 0); + + // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it. const int nav_layer = window->DC.NavLayerCurrent; g.NavId = id; g.NavWindow = window; diff --git a/imgui.h b/imgui.h index 156cb537..f174d83a 100644 --- a/imgui.h +++ b/imgui.h @@ -698,6 +698,7 @@ enum ImGuiKey_ // Your code passing analog gamepad values is likely to want to transform your raw inputs, using a dead-zone and maybe a power curve. enum ImGuiNavInput_ { + // Gamepad Mapping ImGuiNavInput_PadActivate, // press button, tweak value // e.g. Circle button ImGuiNavInput_PadCancel, // close menu/popup/child, lose selection // e.g. Cross button ImGuiNavInput_PadInput, // text input // e.g. Triangle button @@ -714,6 +715,8 @@ enum ImGuiNavInput_ ImGuiNavInput_PadFocusNext, // prev window (with PadMenu held) // e.g. R-trigger ImGuiNavInput_PadTweakSlow, // slower tweaks // e.g. L-trigger, analog ImGuiNavInput_PadTweakFast, // faster tweaks // e.g. R-trigger, analog + // Keyboard Mapping + // [BETA] You can use gamepad mapping for most inputs ImGuiNavInput_KeyMenu, // access menu // e.g. ALT ImGuiNavInput_COUNT, }; @@ -920,7 +923,6 @@ struct ImGuiIO int KeyMap[ImGuiKey_COUNT]; // // Map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. float KeyRepeatDelay; // = 0.250f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. - bool NavMovesMouse; // = false // Directional navigation can move the mouse cursor. Updates MousePos and set WantMoveMouse=true. If enabled you MUST honor those requests in your binding, otherwise ImGui will react as if mouse is jumping around. void* UserData; // = NULL // Store your own data for retrieval by callbacks. ImFontAtlas* Fonts; // // Load and assemble one or more fonts into a single tightly packed texture. Output to Fonts array. @@ -931,6 +933,8 @@ struct ImGuiIO ImVec2 DisplayVisibleMin; // (0.0f,0.0f) // If you use DisplaySize as a virtual space larger than your screen, set DisplayVisibleMin/Max to the visible area. ImVec2 DisplayVisibleMax; // (0.0f,0.0f) // If the values are the same, we defaults to Min=(0.0f) and Max=DisplaySize + // Gamepad/Keyboard Navigation + bool NavMovesMouse; // = false // Directional navigation can move the mouse cursor. Updates MousePos and set WantMoveMouse=true. If enabled you MUST honor those requests in your binding, otherwise ImGui will react as if mouse is jumping around. // Advanced/subtle behaviors bool OptMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl bool OptCursorBlink; // = true // Enable blinking cursor, for users who consider it annoying. From eb7ec781dc88e4517f9abd4a65fa09a52cd8e17e Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 23 Jan 2018 19:41:03 +0100 Subject: [PATCH 236/319] Nav: Tweak GetNavInputAmount(). Split debug defines. --- imgui.cpp | 30 ++++++++++++++++++------------ imgui.h | 2 +- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 01f279af..03c411d5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -645,7 +645,8 @@ #include // intptr_t #endif -#define IMGUI_DEBUG_NAV 0 +#define IMGUI_DEBUG_NAV_SCORING 0 +#define IMGUI_DEBUG_NAV_RECTS 0 #ifdef _MSC_VER #pragma warning (disable: 4127) // condition expression is constant @@ -2197,7 +2198,7 @@ static bool NavScoreItem(ImRect cand) quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; } -#if IMGUI_DEBUG_NAV +#if IMGUI_DEBUG_NAV_SCORING if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) { char buf[128]; @@ -2257,7 +2258,7 @@ static bool NavScoreItem(ImRect cand) static inline void NavUpdateAnyRequestFlag() { ImGuiContext& g = *GImGui; - g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || IMGUI_DEBUG_NAV; + g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || IMGUI_DEBUG_NAV_SCORING; } static void NavMoveRequestCancel() @@ -2294,7 +2295,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con // Scoring for navigation if (g.NavId != id && !(item_flags & ImGuiItemFlags_NoNav)) { -#if IMGUI_DEBUG_NAV +#if IMGUI_DEBUG_NAV_SCORING // [DEBUG] Score all items in NavWindow at all times if (!g.NavMoveRequest) g.NavMoveDir = g.NavMoveDirLast; @@ -2666,12 +2667,15 @@ static float GetNavInputAmount(ImGuiNavInput n, ImGuiNavReadMode mode) { ImGuiContext& g = *GImGui; if (mode == ImGuiNavReadMode_Down) - return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) - const float t = g.IO.NavInputsDownDuration[n]; // Duration pressed - if (mode == ImGuiNavReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input (we don't need it for Pressed logic) + return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) + + const float t = g.IO.NavInputsDownDuration[n]; + if (t < 0.0f && mode == ImGuiNavReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input. + return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f); + if (t < 0.0f) + return 0.0f; + if (mode == ImGuiNavReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input. return (t == 0.0f) ? 1.0f : 0.0f; - if (mode == ImGuiNavReadMode_Released) // Return 1.0f when just release, no repeat, ignore analog input (we don't need it for Pressed logic) - return (t < 0.0f && g.IO.NavInputsDownDurationPrev[n] >= 0.0f) ? 1.0f : 0.0f; if (mode == ImGuiNavReadMode_Repeat) return (float)ImGui::CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f); if (mode == ImGuiNavReadMode_RepeatSlow) @@ -2688,7 +2692,7 @@ static bool IsNavInputDown(ImGuiNavInput n) } // Equivalent of IsKeyPressed() for NavInputs[] -static bool IsNavInputPressed(ImGuiNavInput n, ImGuiNavReadMode mode)// = ImGuiNavReadMode_Re) +static bool IsNavInputPressed(ImGuiNavInput n, ImGuiNavReadMode mode) { return GetNavInputAmount(n, mode) > 0.0f; } @@ -2708,7 +2712,7 @@ static ImVec2 GetNavInputAmount2d(int stick_no, ImGuiNavReadMode mode, float slo return delta; } -// Window management mode (change focus, move/resize window, toggle menu layer) +// Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer) static void ImGui::NavUpdateWindowing() { ImGuiContext& g = *GImGui; @@ -3057,7 +3061,9 @@ static void ImGui::NavUpdate() g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] - //if (g.NavWindow) for (int layer = 0; layer < 2; layer++) g.OverlayDrawList.AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); // [DEBUG] +#if IMGUI_DEBUG_NAV_RECTS + if (g.NavWindow) for (int layer = 0; layer < 2; layer++) g.OverlayDrawList.AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); // [DEBUG] +#endif } void ImGui::NewFrame() diff --git a/imgui.h b/imgui.h index 574a4f88..b17ee558 100644 --- a/imgui.h +++ b/imgui.h @@ -999,7 +999,7 @@ struct ImGuiIO bool WantCaptureKeyboard; // When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. This is set by ImGui when it wants to use your keyboard inputs. bool WantTextInput; // Mobile/console: when io.WantTextInput is true, you may display an on-screen keyboard. This is set by ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). bool WantMoveMouse; // MousePos has been altered, back-end should reposition mouse on next frame. Set only when 'NavMovesMouse=true'. - bool NavUsable; // Directional navigation is currently allowed (will handle ImGuiKey_NavXXX events). + bool NavUsable; // Directional navigation is currently allowed (will handle ImGuiKey_NavXXX events) ~ a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. bool NavActive; // Directional navigation is active/visible and currently allowed (will handle ImGuiKey_NavXXX events). float Framerate; // Application framerate estimation, in frame per second. Solely for convenience. Rolling average estimation based on IO.DeltaTime over 120 frames int MetricsAllocs; // Number of active memory allocations From cea78cc576eae0380f9cf43333e3b7e89730b663 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 24 Jan 2018 10:24:41 +0100 Subject: [PATCH 237/319] Nav: Update hovered logic, so IsItemHovered and ButtonBehavior are more consistent with each other. The known case this fixes is nav focusing on a color button, tooltip appears, mouse move: previously tooltip would stay up. (#787) --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 03c411d5..bafcb158 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2367,7 +2367,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - if (g.NavDisableMouseHover) + if (g.NavDisableMouseHover && !g.NavDisableHighlight) return IsItemFocused(); // Test for bounding box overlap, as updated as ItemAdd() @@ -7331,7 +7331,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // Gamepad/Keyboard navigation // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse. - if (g.NavId == id && !g.NavDisableHighlight && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId)) + if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId)) hovered = true; if (g.NavActivateDownId == id) From 64b786c2aa5e157d846d889963d211385267f2f0 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 24 Jan 2018 10:31:54 +0100 Subject: [PATCH 238/319] Nav: Tweak/fixed popup positioning when using nav without the io.NavMovesMouse flag (it was always assuming a mouse cursor and allocating space for it) (#787) Note that this bit include badly hardcoded sizes, expecting an improvement later. --- imgui.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index bafcb158..06476e2c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5647,7 +5647,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) { ImVec2 ref_pos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos; - ImRect rect_to_avoid(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24, ref_pos.y + 24); // FIXME: Completely hard-coded. Store boxes in mouse cursor data? Scale? Center on cursor hit-point? + ImRect rect_to_avoid; + if (!g.NavDisableHighlight && g.NavDisableMouseHover && !g.IO.NavMovesMouse) + rect_to_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); + else + rect_to_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24, ref_pos.y + 24); // FIXME-NAV: Completely hard-coded based on expected mouse cursor size. Store boxes in mouse cursor data? Scale? Center on cursor hit-point? window->PosFloat = FindBestWindowPosForPopup(ref_pos, window->Size, &window->AutoPosLastDirection, rect_to_avoid); if (window->AutoPosLastDirection == ImGuiDir_None) window->PosFloat = ref_pos + ImVec2(2,2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. From 823e1f0b94a05168d61dbbd0aa1b956c83341efd Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 24 Jan 2018 10:35:47 +0100 Subject: [PATCH 239/319] Nav: Reordered NavInput enums to match directional order of ImGuiDir_ and ImGuiKey_ + comments (#787) --- imgui.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/imgui.h b/imgui.h index b17ee558..f98bc810 100644 --- a/imgui.h +++ b/imgui.h @@ -703,21 +703,21 @@ enum ImGuiNavInput_ ImGuiNavInput_PadActivate, // press button, tweak value // e.g. Circle button ImGuiNavInput_PadCancel, // close menu/popup/child, lose selection // e.g. Cross button ImGuiNavInput_PadInput, // text input // e.g. Triangle button - ImGuiNavInput_PadMenu, // access menu, focus, move, resize // e.g. Square button - ImGuiNavInput_PadUp, // move up, resize window (with PadMenu held) // e.g. D-pad up/down/left/right, analog - ImGuiNavInput_PadDown, // move down - ImGuiNavInput_PadLeft, // move left + ImGuiNavInput_PadMenu, // toggle menu, hold to: focus, move, resize // e.g. Square button + ImGuiNavInput_PadLeft, // move left, resize window (with PadMenu) // e.g. D-pad or left stick directions (analog) ImGuiNavInput_PadRight, // move right - ImGuiNavInput_PadScrollUp, // scroll up, move window (with PadMenu held) // e.g. right stick up/down/left/right, analog - ImGuiNavInput_PadScrollDown, // " - ImGuiNavInput_PadScrollLeft, // - ImGuiNavInput_PadScrollRight, // - ImGuiNavInput_PadFocusPrev, // next window (with PadMenu held) // e.g. L-trigger - ImGuiNavInput_PadFocusNext, // prev window (with PadMenu held) // e.g. R-trigger + ImGuiNavInput_PadUp, // move up + ImGuiNavInput_PadDown, // move down + ImGuiNavInput_PadScrollLeft, // scroll up, move window (with PadMenu) // e.g. right stick directions (analog) + ImGuiNavInput_PadScrollRight, // scroll right + ImGuiNavInput_PadScrollUp, // scroll up + ImGuiNavInput_PadScrollDown, // scroll down + ImGuiNavInput_PadFocusPrev, // next window (with PadMenu) // e.g. L-trigger + ImGuiNavInput_PadFocusNext, // prev window (with PadMenu) // e.g. R-trigger ImGuiNavInput_PadTweakSlow, // slower tweaks // e.g. L-trigger, analog ImGuiNavInput_PadTweakFast, // faster tweaks // e.g. R-trigger, analog // Keyboard Mapping - // [BETA] You can use gamepad mapping for most inputs + // [BETA] You can map keyboard keys on the gamepad mapping for most inputs. Will add specialized keyboard mappings as we add features. ImGuiNavInput_KeyMenu, // access menu // e.g. ALT ImGuiNavInput_COUNT, }; From bd278e958e3fdd53bc0e56818bc826223a9f1a03 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 24 Jan 2018 14:38:33 +0100 Subject: [PATCH 240/319] Nav: Added ImGuiNavInput_KeyLeft/Right/Up/Down set so we can differenciate gamepad/keyboard inputs. (#787) --- imgui.cpp | 45 ++++++++++++++++++++++++++++++--------------- imgui.h | 6 +++++- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 01f8052f..65e623e0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2662,6 +2662,15 @@ enum ImGuiNavReadMode ImGuiNavReadMode_RepeatFast }; +typedef int ImGuiNavDirSource; + +enum ImGuiNavDirSource_ +{ + ImGuiNavDirSource_Key = 1 << 0, + ImGuiNavDirSource_PadStickL = 1 << 1, + ImGuiNavDirSource_PadStickR = 1 << 2 +}; + // FIXME-NAV: Expose navigation repeat delay/rate static float GetNavInputAmount(ImGuiNavInput n, ImGuiNavReadMode mode) { @@ -2697,14 +2706,20 @@ static bool IsNavInputPressed(ImGuiNavInput n, ImGuiNavReadMode mode) return GetNavInputAmount(n, mode) > 0.0f; } -static ImVec2 GetNavInputAmount2d(int stick_no, ImGuiNavReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f) +static bool IsNavInputPressedAnyOfTwo(ImGuiNavInput n1, ImGuiNavInput n2, ImGuiNavReadMode mode) { - IM_ASSERT(ImGuiNavInput_PadScrollUp == ImGuiNavInput_PadUp + 4); - IM_ASSERT(stick_no >= 0 && stick_no < 2); + return (GetNavInputAmount(n1, mode) + GetNavInputAmount(n2, mode)) > 0.0f; +} - ImVec2 delta; - delta.x = GetNavInputAmount(ImGuiNavInput_PadRight + stick_no*4, mode) - GetNavInputAmount(ImGuiNavInput_PadLeft + stick_no*4, mode); - delta.y = GetNavInputAmount(ImGuiNavInput_PadDown + stick_no*4, mode) - GetNavInputAmount(ImGuiNavInput_PadUp + stick_no*4, mode); +static ImVec2 GetNavInputAmount2d(ImGuiNavDirSource dir_sources, ImGuiNavReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f) +{ + ImVec2 delta(0.0f, 0.0f); + if (dir_sources & ImGuiNavDirSource_Key) + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft, mode), GetNavInputAmount(ImGuiNavInput_KeyDown, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp, mode)); + if (dir_sources & ImGuiNavDirSource_PadStickL) + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_PadRight, mode) - GetNavInputAmount(ImGuiNavInput_PadLeft, mode), GetNavInputAmount(ImGuiNavInput_PadDown, mode) - GetNavInputAmount(ImGuiNavInput_PadUp, mode)); + if (dir_sources & ImGuiNavDirSource_PadStickR) + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_PadScrollRight, mode) - GetNavInputAmount(ImGuiNavInput_PadScrollLeft, mode), GetNavInputAmount(ImGuiNavInput_PadScrollDown, mode) - GetNavInputAmount(ImGuiNavInput_PadScrollUp, mode)); if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_PadTweakSlow)) delta *= slow_factor; if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_PadTweakFast)) @@ -2753,7 +2768,7 @@ static void ImGui::NavUpdateWindowing() // Move window if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) { - const ImVec2 move_delta = GetNavInputAmount2d(1, ImGuiNavReadMode_Down); + const ImVec2 move_delta = GetNavInputAmount2d(ImGuiNavDirSource_PadStickR, ImGuiNavReadMode_Down); 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)); @@ -2978,10 +2993,10 @@ static void ImGui::NavUpdate() g.NavMoveDir = ImGuiDir_None; if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { - if ((allowed_dir_flags & (1<ScrollbarX) { SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); @@ -5334,7 +5349,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au // Navigation/gamepad resize if (g.NavWindowingTarget == window) { - ImVec2 nav_resize_delta = GetNavInputAmount2d(0, ImGuiNavReadMode_Down); + ImVec2 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSource_PadStickL, ImGuiNavReadMode_Down); if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) { const float GAMEPAD_RESIZE_SPEED = 600.0f; @@ -8379,7 +8394,7 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v } else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivateDownId == id) { - const ImVec2 delta2 = GetNavInputAmount2d(0, ImGuiNavReadMode_RepeatFast, 0.0f, 0.0f); + const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSource_Key|ImGuiNavDirSource_PadStickL, ImGuiNavReadMode_RepeatFast, 0.0f, 0.0f); float delta = is_horizontal ? delta2.x : -delta2.y; if (delta != 0.0f) { @@ -8726,7 +8741,7 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s } if (g.ActiveIdSource == ImGuiInputSource_Nav) { - adjust_delta = GetNavInputAmount2d(0, ImGuiNavReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; + adjust_delta = GetNavInputAmount2d(ImGuiNavDirSource_Key|ImGuiNavDirSource_PadStickL, ImGuiNavReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); } adjust_delta *= v_speed; diff --git a/imgui.h b/imgui.h index f9a4e002..f6eedaa3 100644 --- a/imgui.h +++ b/imgui.h @@ -718,7 +718,11 @@ enum ImGuiNavInput_ ImGuiNavInput_PadTweakFast, // faster tweaks // e.g. R-trigger, analog // Keyboard Mapping // [BETA] You can map keyboard keys on the gamepad mapping for most inputs. Will add specialized keyboard mappings as we add features. - ImGuiNavInput_KeyMenu, // access menu // e.g. ALT + ImGuiNavInput_KeyMenu, // toggle menu // e.g. ALT + ImGuiNavInput_KeyLeft, // move left // e.g. Arrow keys + ImGuiNavInput_KeyRight, // move right + ImGuiNavInput_KeyUp, // move up + ImGuiNavInput_KeyDown, // move down ImGuiNavInput_COUNT, }; From c09016b12af8e4396c38f06a6cfee2c4e41a18b3 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 24 Jan 2018 14:42:28 +0100 Subject: [PATCH 241/319] Nav: Renamed ImGuiNavInput_PadLeft / etc. to ImGuiNavInput_PadLStickLeft. Renamed ImGuiNavInput_PadScrollLeft to ImGuiNavInput_PadRStickLeft, aka removing trying-too-hard semantic from the enums. (#787) --- imgui.cpp | 32 ++++++++++++++++---------------- imgui.h | 16 ++++++++-------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 65e623e0..24c24687 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2667,8 +2667,8 @@ typedef int ImGuiNavDirSource; enum ImGuiNavDirSource_ { ImGuiNavDirSource_Key = 1 << 0, - ImGuiNavDirSource_PadStickL = 1 << 1, - ImGuiNavDirSource_PadStickR = 1 << 2 + ImGuiNavDirSource_PadLStick = 1 << 1, + ImGuiNavDirSource_PadRStick = 1 << 2 }; // FIXME-NAV: Expose navigation repeat delay/rate @@ -2715,11 +2715,11 @@ static ImVec2 GetNavInputAmount2d(ImGuiNavDirSource dir_sources, ImGuiNavReadMod { ImVec2 delta(0.0f, 0.0f); if (dir_sources & ImGuiNavDirSource_Key) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft, mode), GetNavInputAmount(ImGuiNavInput_KeyDown, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp, mode)); - if (dir_sources & ImGuiNavDirSource_PadStickL) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_PadRight, mode) - GetNavInputAmount(ImGuiNavInput_PadLeft, mode), GetNavInputAmount(ImGuiNavInput_PadDown, mode) - GetNavInputAmount(ImGuiNavInput_PadUp, mode)); - if (dir_sources & ImGuiNavDirSource_PadStickR) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_PadScrollRight, mode) - GetNavInputAmount(ImGuiNavInput_PadScrollLeft, mode), GetNavInputAmount(ImGuiNavInput_PadScrollDown, mode) - GetNavInputAmount(ImGuiNavInput_PadScrollUp, mode)); + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft, mode), GetNavInputAmount(ImGuiNavInput_KeyDown, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp, mode)); + if (dir_sources & ImGuiNavDirSource_PadLStick) + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_PadLStickRight, mode) - GetNavInputAmount(ImGuiNavInput_PadLStickLeft, mode), GetNavInputAmount(ImGuiNavInput_PadLStickDown, mode) - GetNavInputAmount(ImGuiNavInput_PadLStickUp, mode)); + if (dir_sources & ImGuiNavDirSource_PadRStick) + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_PadRStickRight, mode) - GetNavInputAmount(ImGuiNavInput_PadRStickLeft, mode), GetNavInputAmount(ImGuiNavInput_PadRStickDown, mode) - GetNavInputAmount(ImGuiNavInput_PadRStickUp, mode)); if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_PadTweakSlow)) delta *= slow_factor; if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_PadTweakFast)) @@ -2768,7 +2768,7 @@ static void ImGui::NavUpdateWindowing() // Move window if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) { - const ImVec2 move_delta = GetNavInputAmount2d(ImGuiNavDirSource_PadStickR, ImGuiNavReadMode_Down); + const ImVec2 move_delta = GetNavInputAmount2d(ImGuiNavDirSource_PadRStick, ImGuiNavReadMode_Down); 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)); @@ -2993,10 +2993,10 @@ static void ImGui::NavUpdate() g.NavMoveDir = ImGuiDir_None; if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { - if ((allowed_dir_flags & (1<ScrollbarX) { SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); @@ -5349,7 +5349,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au // Navigation/gamepad resize if (g.NavWindowingTarget == window) { - ImVec2 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSource_PadStickL, ImGuiNavReadMode_Down); + ImVec2 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSource_PadLStick, ImGuiNavReadMode_Down); if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) { const float GAMEPAD_RESIZE_SPEED = 600.0f; @@ -8394,7 +8394,7 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v } else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivateDownId == id) { - const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSource_Key|ImGuiNavDirSource_PadStickL, ImGuiNavReadMode_RepeatFast, 0.0f, 0.0f); + const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSource_Key|ImGuiNavDirSource_PadLStick, ImGuiNavReadMode_RepeatFast, 0.0f, 0.0f); float delta = is_horizontal ? delta2.x : -delta2.y; if (delta != 0.0f) { @@ -8741,7 +8741,7 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s } if (g.ActiveIdSource == ImGuiInputSource_Nav) { - adjust_delta = GetNavInputAmount2d(ImGuiNavDirSource_Key|ImGuiNavDirSource_PadStickL, ImGuiNavReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; + adjust_delta = GetNavInputAmount2d(ImGuiNavDirSource_Key|ImGuiNavDirSource_PadLStick, ImGuiNavReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); } adjust_delta *= v_speed; diff --git a/imgui.h b/imgui.h index f6eedaa3..9bb3d7fa 100644 --- a/imgui.h +++ b/imgui.h @@ -704,14 +704,14 @@ enum ImGuiNavInput_ ImGuiNavInput_PadCancel, // close menu/popup/child, lose selection // e.g. Cross button ImGuiNavInput_PadInput, // text input // e.g. Triangle button ImGuiNavInput_PadMenu, // toggle menu, hold to: focus, move, resize // e.g. Square button - ImGuiNavInput_PadLeft, // move left, resize window (with PadMenu) // e.g. D-pad or left stick directions (analog) - ImGuiNavInput_PadRight, // move right - ImGuiNavInput_PadUp, // move up - ImGuiNavInput_PadDown, // move down - ImGuiNavInput_PadScrollLeft, // scroll up, move window (with PadMenu) // e.g. right stick directions (analog) - ImGuiNavInput_PadScrollRight, // scroll right - ImGuiNavInput_PadScrollUp, // scroll up - ImGuiNavInput_PadScrollDown, // scroll down + ImGuiNavInput_PadLStickLeft, // move left, resize window (with PadMenu) // e.g. D-pad + left stick directions (analog) + ImGuiNavInput_PadLStickRight, // move right + ImGuiNavInput_PadLStickUp, // move up + ImGuiNavInput_PadLStickDown, // move down + ImGuiNavInput_PadRStickLeft, // scroll up, move window (with PadMenu) // e.g. right stick directions (analog) + ImGuiNavInput_PadRStickRight, // scroll right + ImGuiNavInput_PadRStickUp, // scroll up + ImGuiNavInput_PadRStickDown, // scroll down ImGuiNavInput_PadFocusPrev, // next window (with PadMenu) // e.g. L-trigger ImGuiNavInput_PadFocusNext, // prev window (with PadMenu) // e.g. R-trigger ImGuiNavInput_PadTweakSlow, // slower tweaks // e.g. L-trigger, analog From 8227176c170ac3f1902f49bf22f433a67fb2a0a3 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 24 Jan 2018 15:03:59 +0100 Subject: [PATCH 242/319] Nav: Menus: Fix for using Left direction inside a menu with widgets layed out horizontally. Left to close is now handled as a fallback inside EndMenu(). (#787) --- imgui.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 24c24687..e5c4aa4f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10842,7 +10842,7 @@ void ImGui::EndMenuBar() return; ImGuiContext& g = *GImGui; - // When a move request within one of our child menu failed, capture the request to navigate among our siblings. + // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. if (g.NavMoveRequest && g.NavMoveResultId == 0 && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) { ImGuiWindow* nav_earliest_child = g.NavWindow; @@ -10926,7 +10926,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) g.NavWindow = backed_nav_window; bool want_open = false, want_close = false; - if (window->DC.LayoutType != ImGuiLayoutType_Horizontal) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) + if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) { // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. bool moving_within_opened_triangle = false; @@ -10960,11 +10960,6 @@ bool ImGui::BeginMenu(const char* label, bool enabled) want_open = true; g.NavMoveRequest = false; } - if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && IsPopupOpen(id)) // Nav-Left to close - { - want_close = true; - g.NavMoveRequest = false; - } } else { @@ -11013,6 +11008,15 @@ bool ImGui::BeginMenu(const char* label, bool enabled) void ImGui::EndMenu() { + // Nav: When a left move request within our child menu failed, close the menu + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveRequest && g.NavMoveResultId == 0 && g.NavMoveDir == ImGuiDir_Left && window->DC.LayoutType == ImGuiLayoutType_Vertical) + { + ClosePopupToLevel(g.OpenPopupStack.Size - 1); + g.NavMoveRequest = false; + } + EndPopup(); } From 28671aa821e37cddce8d0d7a94bd0e972d2f2392 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 24 Jan 2018 16:37:20 +0100 Subject: [PATCH 243/319] Nav: Internals: Moved some enums and functions to internals, renamed ImGuiNavReadMode to ImGuiInputReadMode as well. (#787) --- imgui.cpp | 90 +++++++++++++++++++----------------------------- imgui_internal.h | 21 +++++++++++ 2 files changed, 56 insertions(+), 55 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e5c4aa4f..53811859 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2652,45 +2652,25 @@ static ImGuiWindow* FindWindowNavigable(int i_start, int i_stop, int dir) // FIX return NULL; } -enum ImGuiNavReadMode -{ - ImGuiNavReadMode_Down, - ImGuiNavReadMode_Pressed, - ImGuiNavReadMode_Released, - ImGuiNavReadMode_Repeat, - ImGuiNavReadMode_RepeatSlow, - ImGuiNavReadMode_RepeatFast -}; - -typedef int ImGuiNavDirSource; - -enum ImGuiNavDirSource_ -{ - ImGuiNavDirSource_Key = 1 << 0, - ImGuiNavDirSource_PadLStick = 1 << 1, - ImGuiNavDirSource_PadRStick = 1 << 2 -}; - -// FIXME-NAV: Expose navigation repeat delay/rate -static float GetNavInputAmount(ImGuiNavInput n, ImGuiNavReadMode mode) +float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) { ImGuiContext& g = *GImGui; - if (mode == ImGuiNavReadMode_Down) - return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) + if (mode == ImGuiInputReadMode_Down) + return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) const float t = g.IO.NavInputsDownDuration[n]; - if (t < 0.0f && mode == ImGuiNavReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input. + if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input. return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f); if (t < 0.0f) return 0.0f; - if (mode == ImGuiNavReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input. + if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input. return (t == 0.0f) ? 1.0f : 0.0f; - if (mode == ImGuiNavReadMode_Repeat) - return (float)ImGui::CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f); - if (mode == ImGuiNavReadMode_RepeatSlow) - return (float)ImGui::CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f); - if (mode == ImGuiNavReadMode_RepeatFast) - return (float)ImGui::CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f); + if (mode == ImGuiInputReadMode_Repeat) + return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f); + if (mode == ImGuiInputReadMode_RepeatSlow) + return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f); + if (mode == ImGuiInputReadMode_RepeatFast) + return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f); return 0.0f; } @@ -2701,24 +2681,24 @@ static bool IsNavInputDown(ImGuiNavInput n) } // Equivalent of IsKeyPressed() for NavInputs[] -static bool IsNavInputPressed(ImGuiNavInput n, ImGuiNavReadMode mode) +static bool IsNavInputPressed(ImGuiNavInput n, ImGuiInputReadMode mode) { - return GetNavInputAmount(n, mode) > 0.0f; + return ImGui::GetNavInputAmount(n, mode) > 0.0f; } -static bool IsNavInputPressedAnyOfTwo(ImGuiNavInput n1, ImGuiNavInput n2, ImGuiNavReadMode mode) +static bool IsNavInputPressedAnyOfTwo(ImGuiNavInput n1, ImGuiNavInput n2, ImGuiInputReadMode mode) { - return (GetNavInputAmount(n1, mode) + GetNavInputAmount(n2, mode)) > 0.0f; + return (ImGui::GetNavInputAmount(n1, mode) + ImGui::GetNavInputAmount(n2, mode)) > 0.0f; } -static ImVec2 GetNavInputAmount2d(ImGuiNavDirSource dir_sources, ImGuiNavReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f) +ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor) { ImVec2 delta(0.0f, 0.0f); - if (dir_sources & ImGuiNavDirSource_Key) + if (dir_sources & ImGuiNavDirSourceFlags_Key) delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft, mode), GetNavInputAmount(ImGuiNavInput_KeyDown, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp, mode)); - if (dir_sources & ImGuiNavDirSource_PadLStick) + if (dir_sources & ImGuiNavDirSourceFlags_PadLStick) delta += ImVec2(GetNavInputAmount(ImGuiNavInput_PadLStickRight, mode) - GetNavInputAmount(ImGuiNavInput_PadLStickLeft, mode), GetNavInputAmount(ImGuiNavInput_PadLStickDown, mode) - GetNavInputAmount(ImGuiNavInput_PadLStickUp, mode)); - if (dir_sources & ImGuiNavDirSource_PadRStick) + if (dir_sources & ImGuiNavDirSourceFlags_PadRStick) delta += ImVec2(GetNavInputAmount(ImGuiNavInput_PadRStickRight, mode) - GetNavInputAmount(ImGuiNavInput_PadRStickLeft, mode), GetNavInputAmount(ImGuiNavInput_PadRStickDown, mode) - GetNavInputAmount(ImGuiNavInput_PadRStickUp, mode)); if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_PadTweakSlow)) delta *= slow_factor; @@ -2733,7 +2713,7 @@ static void ImGui::NavUpdateWindowing() ImGuiContext& g = *GImGui; bool toggle_layer = false; - if (!g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiNavReadMode_Pressed)) + if (!g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiInputReadMode_Pressed)) { ImGuiWindow* window = g.NavWindow; if (!window) @@ -2753,7 +2733,7 @@ static void ImGui::NavUpdateWindowing() g.NavWindowingToggleLayer &= (g.NavWindowingDisplayAlpha < 1.0f); // Once button is held long enough we don't consider it a tag-to-toggle-layer press anymore. // Select window to focus - const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_PadFocusPrev, ImGuiNavReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_PadFocusNext, ImGuiNavReadMode_RepeatSlow); + const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_PadFocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_PadFocusNext, ImGuiInputReadMode_RepeatSlow); if (focus_change_dir != 0 && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)) { const int i_current = FindWindowIndex(g.NavWindowingTarget); @@ -2768,7 +2748,7 @@ static void ImGui::NavUpdateWindowing() // Move window if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) { - const ImVec2 move_delta = GetNavInputAmount2d(ImGuiNavDirSource_PadRStick, ImGuiNavReadMode_Down); + const ImVec2 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadRStick, ImGuiInputReadMode_Down); 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)); @@ -2802,7 +2782,7 @@ static void ImGui::NavUpdateWindowing() } // Keyboard: Press and release ALT to toggle menu - if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu, ImGuiNavReadMode_Released)) + if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu, ImGuiInputReadMode_Released)) toggle_layer = true; if (toggle_layer && g.NavWindow) @@ -2921,7 +2901,7 @@ static void ImGui::NavUpdate() g.IO.NavActive = (g.IO.NavUsable && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; // Process NavCancel input (to close a popup, get back to parent, clear focus) - if (IsNavInputPressed(ImGuiNavInput_PadCancel, ImGuiNavReadMode_Pressed)) + if (IsNavInputPressed(ImGuiNavInput_PadCancel, ImGuiInputReadMode_Pressed)) { if (g.ActiveId != 0) { @@ -2965,11 +2945,11 @@ static void ImGui::NavUpdate() g.NavActivateId = g.NavActivateDownId = g.NavInputId = 0; if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget) { - if (g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadActivate, ImGuiNavReadMode_Pressed)) + if (g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadActivate, ImGuiInputReadMode_Pressed)) g.NavActivateId = g.NavId; if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputDown(ImGuiNavInput_PadActivate)) g.NavActivateDownId = g.NavId; - if (g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadInput, ImGuiNavReadMode_Pressed)) + if (g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadInput, ImGuiInputReadMode_Pressed)) g.NavInputId = g.NavId; } if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) @@ -2993,10 +2973,10 @@ static void ImGui::NavUpdate() g.NavMoveDir = ImGuiDir_None; if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { - if ((allowed_dir_flags & (1<ScrollbarX) { SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); @@ -5349,7 +5329,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au // Navigation/gamepad resize if (g.NavWindowingTarget == window) { - ImVec2 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSource_PadLStick, ImGuiNavReadMode_Down); + ImVec2 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) { const float GAMEPAD_RESIZE_SPEED = 600.0f; @@ -7359,7 +7339,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if (g.NavActivateDownId == id) { bool nav_activated_by_code = (g.NavActivateId == id); - bool nav_activated_by_inputs = IsNavInputPressed(ImGuiNavInput_PadActivate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiNavReadMode_Repeat : ImGuiNavReadMode_Pressed); + bool nav_activated_by_inputs = IsNavInputPressed(ImGuiNavInput_PadActivate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed); if (nav_activated_by_code || nav_activated_by_inputs) pressed = true; if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id) @@ -8394,7 +8374,7 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v } else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivateDownId == id) { - const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSource_Key|ImGuiNavDirSource_PadLStick, ImGuiNavReadMode_RepeatFast, 0.0f, 0.0f); + const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Key|ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f); float delta = is_horizontal ? delta2.x : -delta2.y; if (delta != 0.0f) { @@ -8741,7 +8721,7 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s } if (g.ActiveIdSource == ImGuiInputSource_Nav) { - adjust_delta = GetNavInputAmount2d(ImGuiNavDirSource_Key|ImGuiNavDirSource_PadLStick, ImGuiNavReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; + adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Key|ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); } adjust_delta *= v_speed; diff --git a/imgui_internal.h b/imgui_internal.h index 693f03c9..c94cb222 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -47,6 +47,7 @@ typedef int ImGuiLayoutType; // enum: horizontal or vertical typedef int ImGuiButtonFlags; // flags: for ButtonEx(), ButtonBehavior() // enum ImGuiButtonFlags_ typedef int ImGuiItemFlags; // flags: for PushItemFlag() // enum ImGuiItemFlags_ typedef int ImGuiNavHighlightFlags; // flags: for RenderNavHighlight() // enum ImGuiNavHighlightFlags_ +typedef int ImGuiNavDirSourceFlags; // flags: for GetNavInputAmount2d() // enum ImGuiNavDirSourceFlags_ typedef int ImGuiSeparatorFlags; // flags: for Separator() - internal // enum ImGuiSeparatorFlags_ typedef int ImGuiSliderFlags; // flags: for SliderBehavior() // enum ImGuiSliderFlags_ @@ -254,6 +255,17 @@ enum ImGuiInputSource ImGuiInputSource_Count_, }; +// FIXME-NAV: Clarify/expose various repeat delay/rate +enum ImGuiInputReadMode +{ + ImGuiInputReadMode_Down, + ImGuiInputReadMode_Pressed, + ImGuiInputReadMode_Released, + ImGuiInputReadMode_Repeat, + ImGuiInputReadMode_RepeatSlow, + ImGuiInputReadMode_RepeatFast +}; + enum ImGuiDir { ImGuiDir_None = -1, @@ -271,6 +283,13 @@ enum ImGuiNavHighlightFlags_ ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2 }; +enum ImGuiNavDirSourceFlags_ +{ + ImGuiNavDirSourceFlags_Key = 1 << 0, + ImGuiNavDirSourceFlags_PadLStick = 1 << 1, + ImGuiNavDirSourceFlags_PadRStick = 1 << 2 +}; + // 2D axis aligned bounding-box // NB: we can't rely on ImVec2 math operators being available here struct IMGUI_API ImRect @@ -1001,6 +1020,8 @@ namespace ImGui IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. + IMGUI_API float GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode); + IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f); IMGUI_API int CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate); IMGUI_API void Scrollbar(ImGuiLayoutType direction); From 455989b8b1534947886d9650e9ae44386e311121 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 24 Jan 2018 16:57:16 +0100 Subject: [PATCH 244/319] Nav: Added io.NavFlags to hold various options. Added ImGuiNavFlags_EnableGamepad and ImGuiNavFlags_EnableKeyboard for bindings to use (#787) --- imgui.cpp | 7 ++++--- imgui.h | 12 ++++++++++-- imgui_demo.cpp | 12 +++++++----- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 53811859..f330711e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -830,13 +830,13 @@ ImGuiIO::ImGuiIO() IniSavingRate = 5.0f; IniFilename = "imgui.ini"; LogFilename = "imgui_log.txt"; + NavFlags = 0x00; MouseDoubleClickTime = 0.30f; MouseDoubleClickMaxDist = 6.0f; for (int i = 0; i < ImGuiKey_COUNT; i++) KeyMap[i] = -1; KeyRepeatDelay = 0.250f; KeyRepeatRate = 0.050f; - NavMovesMouse = false; UserData = NULL; Fonts = &GImDefaultFontAtlas; @@ -2883,7 +2883,7 @@ static void ImGui::NavUpdate() if (g.NavMousePosDirty && g.NavIdIsAlive) { // Set mouse position given our knowledge of the nav widget position from last frame - if (g.IO.NavMovesMouse) + if (g.IO.NavFlags & ImGuiNavFlags_MoveMouse) { g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredMousePos(); g.IO.WantMoveMouse = true; @@ -5643,7 +5643,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { ImVec2 ref_pos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos; ImRect rect_to_avoid; - if (!g.NavDisableHighlight && g.NavDisableMouseHover && !g.IO.NavMovesMouse) + if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.NavFlags & ImGuiNavFlags_MoveMouse)) rect_to_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); else rect_to_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24, ref_pos.y + 24); // FIXME-NAV: Completely hard-coded based on expected mouse cursor size. Store boxes in mouse cursor data? Scale? Center on cursor hit-point? @@ -7288,6 +7288,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0)) hovered = false; + // Mouse if (hovered) { if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt)) diff --git a/imgui.h b/imgui.h index 9bb3d7fa..4eef8b05 100644 --- a/imgui.h +++ b/imgui.h @@ -90,6 +90,7 @@ typedef int ImGuiComboFlags; // flags: for BeginCombo() typedef int ImGuiFocusedFlags; // flags: for IsWindowFocused() // enum ImGuiFocusedFlags_ typedef int ImGuiHoveredFlags; // flags: for IsItemHovered() etc. // enum ImGuiHoveredFlags_ typedef int ImGuiInputTextFlags; // flags: for InputText*() // enum ImGuiInputTextFlags_ +typedef int ImGuiNavFlags; // flags: for io.NavFlags // enum ImGuiNavFlags_ typedef int ImGuiSelectableFlags; // flags: for Selectable() // enum ImGuiSelectableFlags_ typedef int ImGuiTreeNodeFlags; // flags: for TreeNode*(),CollapsingHeader()// enum ImGuiTreeNodeFlags_ typedef int ImGuiWindowFlags; // flags: for Begin*() // enum ImGuiWindowFlags_ @@ -726,6 +727,14 @@ enum ImGuiNavInput_ ImGuiNavInput_COUNT, }; +// [BETA] Gamepad/Keyboard directional navigation options +enum ImGuiNavFlags_ +{ + ImGuiNavFlags_EnableGamepad = 1 << 0, // Master gamepad navigation enable flag. This is to instruct your imgui binding whether to fill in gamepad navigation inputs. imgui itself won't use this flag. + ImGuiNavFlags_EnableKeyboard = 1 << 1, // Master keyboard navigation enable flag. This is to instruct your imgui binding whether to fill in keyboard navigation inputs. imgui itself won't use this flag. + ImGuiNavFlags_MoveMouse = 1 << 2 // Allow directional navigation to move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantMoveMouse=true. If enabled you MUST honor io.WantMoveMouse requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. +}; + // Enumeration for PushStyleColor() / PopStyleColor() enum ImGuiCol_ { @@ -926,6 +935,7 @@ struct ImGuiIO float IniSavingRate; // = 5.0f // Maximum time between saving positions/sizes to .ini file, in seconds. const char* IniFilename; // = "imgui.ini" // Path to .ini file. NULL to disable .ini saving. const char* LogFilename; // = "imgui_log.txt" // Path to .log file (default parameter to ImGui::LogToFile when no file is specified). + ImGuiNavFlags NavFlags; // = 0 // See ImGuiNavFlags_. Gamepad/keyboard navigation options. float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. @@ -942,8 +952,6 @@ struct ImGuiIO ImVec2 DisplayVisibleMin; // (0.0f,0.0f) // If you use DisplaySize as a virtual space larger than your screen, set DisplayVisibleMin/Max to the visible area. ImVec2 DisplayVisibleMax; // (0.0f,0.0f) // If the values are the same, we defaults to Min=(0.0f) and Max=DisplaySize - // Gamepad/Keyboard Navigation - bool NavMovesMouse; // = false // Directional navigation can move the mouse cursor. Updates MousePos and set WantMoveMouse=true. If enabled you MUST honor those requests in your binding, otherwise ImGui will react as if mouse is jumping around. // Advanced/subtle behaviors bool OptMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl bool OptCursorBlink; // = true // Enable blinking cursor, for users who consider it annoying. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 574f49ea..47397269 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1788,17 +1788,19 @@ void ImGui::ShowDemoWindow(bool* p_open) if (ImGui::CollapsingHeader("Inputs, Navigation & Focus")) { ImGuiIO& io = ImGui::GetIO(); - ImGui::Checkbox("io.NavMovesMouse", &io.NavMovesMouse); - ImGui::SameLine(); ShowHelpMarker("Request ImGui to move your move cursor when using gamepad/keyboard navigation. NewFrame() will change io.MousePos and set the io.WantMoveMouse flag, your backend will need to apply the new mouse position."); - - ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); - ImGui::SameLine(); ShowHelpMarker("Request ImGui to render a mouse cursor for you in software. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); ImGui::Text("WantCaptureMouse: %d", io.WantCaptureMouse); ImGui::Text("WantCaptureKeyboard: %d", io.WantCaptureKeyboard); ImGui::Text("WantTextInput: %d", io.WantTextInput); ImGui::Text("WantMoveMouse: %d", io.WantMoveMouse); + ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); + ImGui::SameLine(); ShowHelpMarker("Request ImGui to render a mouse cursor for you in software. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); + ImGui::CheckboxFlags("io.NavFlags: EnableGamepad", (unsigned int *)&io.NavFlags, ImGuiNavFlags_EnableGamepad); + ImGui::CheckboxFlags("io.NavFlags: EnableKeyboard", (unsigned int *)&io.NavFlags, ImGuiNavFlags_EnableKeyboard); + ImGui::CheckboxFlags("io.NavFlags: MoveMouse", (unsigned int *)&io.NavFlags, ImGuiNavFlags_MoveMouse); + ImGui::SameLine(); ShowHelpMarker("Request ImGui to move your move cursor when using gamepad/keyboard navigation. NewFrame() will change io.MousePos and set the io.WantMoveMouse flag, your backend will need to apply the new mouse position."); + if (ImGui::TreeNode("Keyboard, Mouse & Navigation State")) { if (ImGui::IsMousePosValid()) From 6f366fff6b130c994d366e479302baed06370598 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 24 Jan 2018 17:08:37 +0100 Subject: [PATCH 245/319] Demo: Tweaked example menu with colors + menu items, was currently particularly inconvenient for Nav. Will rework later. (#787) --- imgui_demo.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 47397269..a642e5d1 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2330,15 +2330,16 @@ static void ShowExampleMenuFile() } if (ImGui::BeginMenu("Colors")) { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); + float sz = ImGui::GetTextLineHeight(); for (int i = 0; i < ImGuiCol_COUNT; i++) { const char* name = ImGui::GetStyleColorName((ImGuiCol)i); - ImGui::ColorButton(name, ImGui::GetStyleColorVec4((ImGuiCol)i)); + ImVec2 p = ImGui::GetCursorScreenPos(); + ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x+sz, p.y+sz), ImGui::GetColorU32((ImGuiCol)i)); + ImGui::Dummy(ImVec2(sz, sz)); ImGui::SameLine(); ImGui::MenuItem(name); } - ImGui::PopStyleVar(); ImGui::EndMenu(); } if (ImGui::BeginMenu("Disabled", false)) // Disabled From 89b0ca1f8f044324aa7836ad98856fd2602273d1 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 24 Jan 2018 19:55:56 +0100 Subject: [PATCH 246/319] Nav: FocusWindow() doesn't reset NavRectRel (which was flickering e.g. when returning to a parent menu). This was added in the initial nav branch commit and I don't see a reason for it. (#787) --- imgui.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f330711e..7e19e18b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3057,7 +3057,8 @@ static void ImGui::NavUpdate() g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] #if IMGUI_DEBUG_NAV_RECTS - if (g.NavWindow) for (int layer = 0; layer < 2; layer++) g.OverlayDrawList.AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); // [DEBUG] + if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) g.OverlayDrawList.AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] + if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames <= 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredMousePos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); g.OverlayDrawList.AddCircleFilled(p, 3.0f, col); g.OverlayDrawList.AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } #endif } @@ -6151,8 +6152,6 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavLayer = 0; if (window && g.NavDisableMouseHover) g.NavMousePosDirty = true; - if (window) - window->NavRectRel[0].Min = window->NavRectRel[0].Max = window->DC.CursorStartPos - window->Pos; g.NavWindow = window; } From d404b93b6bbcc6a652f14f43ee68588853449602 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 24 Jan 2018 19:57:39 +0100 Subject: [PATCH 247/319] Nav: Mouse clicking on a window (to select/move) disables hides nav highlight. (#787) + comments --- imgui.cpp | 1 + imgui_internal.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 7e19e18b..56060c4e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3791,6 +3791,7 @@ void ImGui::EndFrame() // Set ActiveId even if the _NoMove flag is set, without it dragging away from a window with _NoMove would activate hover on other windows. FocusWindow(g.HoveredWindow); SetActiveID(g.HoveredWindow->MoveId, g.HoveredWindow); + g.NavDisableHighlight = true; g.ActiveIdClickOffset = g.IO.MousePos - g.HoveredRootWindow->Pos; if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove) && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoMove)) g.MovingWindow = g.HoveredWindow; diff --git a/imgui_internal.h b/imgui_internal.h index c94cb222..a3def317 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -905,7 +905,7 @@ struct IMGUI_API ImGuiWindow ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0,0) when positioning from top-left corner; ImVec2(0.5f,0.5f) for centering; ImVec2(1,1) for bottom right. ImGuiID NavLastIds[2]; // Last known NavId for this window, per layer (0/1) - ImRect NavRectRel[2]; // Reference rectangle, in window space + ImRect NavRectRel[2]; // Reference rectangle, in window relative space ImGuiDrawContext DC; // Temporary per-window data, reset at the beginning of the frame ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack From 5f7f27c8de469192d80918b736fbaed85eccd28f Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 24 Jan 2018 20:05:36 +0100 Subject: [PATCH 248/319] Nav: Comments. (#787) --- imgui.cpp | 27 ++++++++++----------------- imgui.h | 4 ++-- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 56060c4e..0886e04f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -217,29 +217,22 @@ - Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. - Consider using Synergy host (on your computer) + uSynergy.c (in your console/tablet/phone app) to use PC mouse/keyboard. - Your inputs are passed to imgui by filling the io.NavInputs[] array. See 'enum ImGuiNavInput_' in imgui.h for a description of available inputs. + - The ImGuiNavFlags_EnableGamepad and ImGuiNavFlags_EnableKeyboard flags of io.NavFlags are only here to instruct your binding whether to find inputs. - For gamepad use, the easiest approach is to go all-or-nothing, with a buttons combo that toggle your inputs between imgui and your game/application. Sharing inputs in a more advanced or granular way between imgui and your game/application may be tricky and requires further work on imgui. For more advanced uses, you may want to use: - io.NavUsable: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. - io.NavActive: true when the navigation cursor is visible (and usually goes false when mouse is used). - query focus information with IsWindowFocused(), IsAnyWindowFocused(), IsAnyItemFocused() functions. - The reality is more complex than what those flags can express. Please discuss your issues and usage scenario in the thread above. + The reality is more complex than what those flags can express. Please discuss your issues and usage scenario in the thread above! As we head toward more keyboard-oriented development this aspect will need to be improved. - - It is recommended that you enable the 'io.NavMovesMouse' option. Enabling it instructs ImGui that it can move your move cursor to track navigated items and ease readability. - When enabled and using directional navigation (with d-pad or arrow keys), the NewFrame() functions may alter 'io.MousePos' and set 'io.WantMoveMouse' to notify you that it did so. - When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. The examples binding in examples/ do that. - (Important: It you set 'io.NavMovesMouse' to true but don't honor 'io.WantMoveMouse' properly, imgui will misbehave as it will think your mouse is moving back and forth.) - - // Application init - io.NavMovesMouse = true; - - // Application main loop - if (io.WantMoveMouse) - MyFuncToSetMousePosition(io.MousePos.x, io.MousePos.y); - ImGui::NewFrame(); - - In a setup when you may not have easy control over the mouse cursor (e.g. uSynergy.c doesn't expose moving remote mouse cursor), - you might want to set a boolean to ignore your other external mouse positions until they move again. + - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiNavFlags_MoveMouse flag in io.NavFlags. + Enabling ImGuiNavFlags_MoveMouse instructs dear imgui to move your mouse cursor along when navigation movement. + When enabled, the NewFrame() functions may alter 'io.MousePos' and set 'io.WantMoveMouse' to notify you that it did so. + When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that. + (Important: It you set the ImGuiNavFlags_MoveMouse flag but don't honor 'io.WantMoveMouse' properly, imgui will misbehave as it will think your mouse is moving back and forth.) + (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want + to set a boolean to ignore your other external mouse positions until the external source is moved again.) API BREAKING CHANGES @@ -3135,7 +3128,7 @@ void ImGui::NewFrame() for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f; - // Update directional navigation which may override MousePos if 'NavMovesMouse=true' + // Update directional navigation which may override MousePos if ImGuiNavFlags_MoveMouse is enabled. NavUpdate(); // Update mouse input state diff --git a/imgui.h b/imgui.h index 4eef8b05..c5e4e39d 100644 --- a/imgui.h +++ b/imgui.h @@ -732,7 +732,7 @@ enum ImGuiNavFlags_ { ImGuiNavFlags_EnableGamepad = 1 << 0, // Master gamepad navigation enable flag. This is to instruct your imgui binding whether to fill in gamepad navigation inputs. imgui itself won't use this flag. ImGuiNavFlags_EnableKeyboard = 1 << 1, // Master keyboard navigation enable flag. This is to instruct your imgui binding whether to fill in keyboard navigation inputs. imgui itself won't use this flag. - ImGuiNavFlags_MoveMouse = 1 << 2 // Allow directional navigation to move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantMoveMouse=true. If enabled you MUST honor io.WantMoveMouse requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. + ImGuiNavFlags_MoveMouse = 1 << 2 // Request navigation to allow move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantMoveMouse=true. If enabled you MUST honor io.WantMoveMouse requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. }; // Enumeration for PushStyleColor() / PopStyleColor() @@ -1010,7 +1010,7 @@ struct ImGuiIO bool WantCaptureMouse; // When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. This is set by ImGui when it wants to use your mouse (e.g. unclicked mouse is hovering a window, or a widget is active). bool WantCaptureKeyboard; // When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. This is set by ImGui when it wants to use your keyboard inputs. bool WantTextInput; // Mobile/console: when io.WantTextInput is true, you may display an on-screen keyboard. This is set by ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). - bool WantMoveMouse; // MousePos has been altered, back-end should reposition mouse on next frame. Set only when 'NavMovesMouse=true'. + bool WantMoveMouse; // MousePos has been altered, back-end should reposition mouse on next frame. Set only when ImGuiNavFlags_MoveMouse flag is enabled in io.NavFlags. bool NavUsable; // Directional navigation is currently allowed (will handle ImGuiKey_NavXXX events) ~ a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. bool NavActive; // Directional navigation is active/visible and currently allowed (will handle ImGuiKey_NavXXX events). float Framerate; // Application framerate estimation, in frame per second. Solely for convenience. Rolling average estimation based on IO.DeltaTime over 120 frames From 3967ff58b2d82afb6db5fc85fe0bbf0a09a5fb7b Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 25 Jan 2018 15:48:30 +0100 Subject: [PATCH 249/319] Nav: Examples: DirectX11, Glfw+GL3: Basic code to map keyboard inputs when io.NavFlags & ImGuiNavFlags_EnableKeyboard is set. (will iterate/tweak before spreading to other examples). (#787) --- .../directx11_example/imgui_impl_dx11.cpp | 20 +++++++++++++++++ examples/directx11_example/main.cpp | 3 ++- .../opengl3_example/imgui_impl_glfw_gl3.cpp | 22 +++++++++++++++++++ examples/opengl3_example/main.cpp | 3 ++- 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/examples/directx11_example/imgui_impl_dx11.cpp b/examples/directx11_example/imgui_impl_dx11.cpp index 4963fc12..cd57bc97 100644 --- a/examples/directx11_example/imgui_impl_dx11.cpp +++ b/examples/directx11_example/imgui_impl_dx11.cpp @@ -605,6 +605,26 @@ void ImGui_ImplDX11_NewFrame() // io.MouseDown : filled by WM_*BUTTON* events // io.MouseWheel : filled by WM_MOUSEWHEEL events + // Gamepad/keyboard navigation mapping [BETA] + memset(io.NavInputs, 0, sizeof(io.NavInputs)); + if (io.NavFlags & ImGuiNavFlags_EnableKeyboard) + { + // Update keyboard + // FIXME-NAV: We are still using some of the ImGuiNavInput_PadXXX enums as keyboard support is incomplete. + #define MAP_KEY(NAV_NO, KEY_NO) { if (io.KeysDown[KEY_NO]) io.NavInputs[NAV_NO] = 1.0f; } + MAP_KEY(ImGuiNavInput_KeyLeft, VK_LEFT); + MAP_KEY(ImGuiNavInput_KeyRight, VK_RIGHT); + MAP_KEY(ImGuiNavInput_KeyUp, VK_UP); + MAP_KEY(ImGuiNavInput_KeyDown, VK_DOWN); + MAP_KEY(ImGuiNavInput_KeyMenu, VK_MENU); + MAP_KEY(ImGuiNavInput_PadActivate, VK_SPACE); + MAP_KEY(ImGuiNavInput_PadCancel, VK_ESCAPE); + MAP_KEY(ImGuiNavInput_PadInput, VK_RETURN); + MAP_KEY(ImGuiNavInput_PadTweakFast, VK_SHIFT); + MAP_KEY(ImGuiNavInput_PadTweakSlow, VK_CONTROL); + #undef MAP_KEY + } + // Set OS mouse position if requested last frame by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) if (io.WantMoveMouse) { diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index 79e7bb16..1ce5d49d 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -125,7 +125,9 @@ int main(int, char**) UpdateWindow(hwnd); // Setup ImGui binding + ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui_ImplDX11_Init(hwnd, g_pd3dDevice, g_pd3dDeviceContext); + //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Setup style ImGui::StyleColorsClassic(); @@ -138,7 +140,6 @@ int main(int, char**) // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'extra_fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //ImGuiIO& io = ImGui::GetIO(); //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../extra_fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../extra_fonts/Cousine-Regular.ttf", 15.0f); diff --git a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp index a71fc515..196f6413 100644 --- a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp +++ b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp @@ -411,6 +411,28 @@ void ImGui_ImplGlfwGL3_NewFrame() // Hide OS mouse cursor if ImGui is drawing it glfwSetInputMode(g_Window, GLFW_CURSOR, io.MouseDrawCursor ? GLFW_CURSOR_HIDDEN : GLFW_CURSOR_NORMAL); + // Gamepad/keyboard navigation mapping [BETA] + memset(io.NavInputs, 0, sizeof(io.NavInputs)); + if (io.NavFlags & ImGuiNavFlags_EnableKeyboard) + { + // Update keyboard + // FIXME-NAV: We are still using some of the ImGuiNavInput_PadXXX enums as keyboard support is incomplete. + #define MAP_KEY(NAV_NO, KEY_NO) { if (io.KeysDown[KEY_NO]) io.NavInputs[NAV_NO] = 1.0f; } + MAP_KEY(ImGuiNavInput_KeyLeft, GLFW_KEY_LEFT); + MAP_KEY(ImGuiNavInput_KeyRight, GLFW_KEY_RIGHT); + MAP_KEY(ImGuiNavInput_KeyUp, GLFW_KEY_UP); + MAP_KEY(ImGuiNavInput_KeyDown, GLFW_KEY_DOWN); + MAP_KEY(ImGuiNavInput_KeyMenu, GLFW_KEY_LEFT_ALT); + MAP_KEY(ImGuiNavInput_PadActivate, GLFW_KEY_SPACE); + MAP_KEY(ImGuiNavInput_PadCancel, GLFW_KEY_ESCAPE); + MAP_KEY(ImGuiNavInput_PadInput, GLFW_KEY_ENTER); + MAP_KEY(ImGuiNavInput_PadTweakSlow, GLFW_KEY_LEFT_ALT); + MAP_KEY(ImGuiNavInput_PadTweakSlow, GLFW_KEY_RIGHT_ALT); + MAP_KEY(ImGuiNavInput_PadTweakFast, GLFW_KEY_LEFT_SHIFT); + MAP_KEY(ImGuiNavInput_PadTweakFast, GLFW_KEY_RIGHT_SHIFT); + #undef MAP_KEY + } + // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. ImGui::NewFrame(); } diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index 6beb855d..4b3073af 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -32,7 +32,9 @@ int main(int, char**) gl3wInit(); // Setup ImGui binding + ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui_ImplGlfwGL3_Init(window, true); + //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Setup style ImGui::StyleColorsClassic(); @@ -45,7 +47,6 @@ int main(int, char**) // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'extra_fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //ImGuiIO& io = ImGui::GetIO(); //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../extra_fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../extra_fonts/Cousine-Regular.ttf", 15.0f); From e9070e768ef99303ae72f0f3be3c10c8dc0e2d66 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 25 Jan 2018 16:17:36 +0100 Subject: [PATCH 250/319] Nav: Fixed renaming of c09016b12af8e4396c38f06a6cfee2c4e41a18b3 that were incorrect. ImGuiNavInput_PadLeft -> PadDpadLeft, _PadScrollLeft -> PadLStickLeft. (#787) --- imgui.cpp | 14 +++++++------- imgui.h | 16 ++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0886e04f..c0ccf1f7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2688,11 +2688,11 @@ ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInput { ImVec2 delta(0.0f, 0.0f); if (dir_sources & ImGuiNavDirSourceFlags_Key) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft, mode), GetNavInputAmount(ImGuiNavInput_KeyDown, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp, mode)); + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft, mode), GetNavInputAmount(ImGuiNavInput_KeyDown, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp, mode)); if (dir_sources & ImGuiNavDirSourceFlags_PadLStick) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_PadLStickRight, mode) - GetNavInputAmount(ImGuiNavInput_PadLStickLeft, mode), GetNavInputAmount(ImGuiNavInput_PadLStickDown, mode) - GetNavInputAmount(ImGuiNavInput_PadLStickUp, mode)); + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_PadDpadRight, mode) - GetNavInputAmount(ImGuiNavInput_PadDpadLeft, mode), GetNavInputAmount(ImGuiNavInput_PadDpadDown, mode) - GetNavInputAmount(ImGuiNavInput_PadDpadUp, mode)); if (dir_sources & ImGuiNavDirSourceFlags_PadRStick) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_PadRStickRight, mode) - GetNavInputAmount(ImGuiNavInput_PadRStickLeft, mode), GetNavInputAmount(ImGuiNavInput_PadRStickDown, mode) - GetNavInputAmount(ImGuiNavInput_PadRStickUp, mode)); + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_PadLStickRight, mode) - GetNavInputAmount(ImGuiNavInput_PadLStickLeft, mode), GetNavInputAmount(ImGuiNavInput_PadLStickDown, mode) - GetNavInputAmount(ImGuiNavInput_PadLStickUp, mode)); if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_PadTweakSlow)) delta *= slow_factor; if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_PadTweakFast)) @@ -2966,10 +2966,10 @@ static void ImGui::NavUpdate() g.NavMoveDir = ImGuiDir_None; if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { - if ((allowed_dir_flags & (1< Date: Thu, 25 Jan 2018 16:18:57 +0100 Subject: [PATCH 251/319] Nav: Examples: Glfw+GL3: Added basic gamepad mapping code when io.NavFlags & ImGuiNavFlags_EnableGamepad is set. (will iterate/tweak before spreading to other examples). (#787) --- .../opengl3_example/imgui_impl_glfw_gl3.cpp | 27 +++++++++++++++++++ examples/opengl3_example/main.cpp | 1 + 2 files changed, 28 insertions(+) diff --git a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp index 196f6413..5e9f1787 100644 --- a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp +++ b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp @@ -432,6 +432,33 @@ void ImGui_ImplGlfwGL3_NewFrame() MAP_KEY(ImGuiNavInput_PadTweakFast, GLFW_KEY_RIGHT_SHIFT); #undef MAP_KEY } + if (io.NavFlags & ImGuiNavFlags_EnableGamepad) + { + // Update gamepad inputs + #define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; } + #define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; } + int axes_count = 0, buttons_count = 0; + const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); + const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); + MAP_BUTTON(ImGuiNavInput_PadActivate, 0); // Cross / A + MAP_BUTTON(ImGuiNavInput_PadCancel, 1); // Circle / B + MAP_BUTTON(ImGuiNavInput_PadMenu, 2); // Square / X + MAP_BUTTON(ImGuiNavInput_PadInput, 3); // Triangle / Y + MAP_BUTTON(ImGuiNavInput_PadDpadLeft, 13); // D-Pad Left + MAP_BUTTON(ImGuiNavInput_PadDpadRight, 11); // D-Pad Right + MAP_BUTTON(ImGuiNavInput_PadDpadUp, 10); // D-Pad Up + MAP_BUTTON(ImGuiNavInput_PadDpadDown, 12); // D-Pad Down + MAP_BUTTON(ImGuiNavInput_PadFocusPrev, 4); // L Trigger + MAP_BUTTON(ImGuiNavInput_PadFocusNext, 5); // R Trigger + MAP_BUTTON(ImGuiNavInput_PadTweakSlow, 4); // L Trigger + MAP_BUTTON(ImGuiNavInput_PadTweakFast, 5); // R Trigger + MAP_ANALOG(ImGuiNavInput_PadLStickLeft, 0, -0.3f, -0.9f); + MAP_ANALOG(ImGuiNavInput_PadLStickRight,0, +0.3f, +0.9f); + MAP_ANALOG(ImGuiNavInput_PadLStickUp, 1, +0.3f, +0.9f); + MAP_ANALOG(ImGuiNavInput_PadLStickDown, 1, -0.3f, -0.9f); + #undef MAP_BUTTON + #undef MAP_ANALOG + } // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. ImGui::NewFrame(); diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index 4b3073af..7b5f1c56 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -35,6 +35,7 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui_ImplGlfwGL3_Init(window, true); //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; + //io.NavFlags |= ImGuiNavFlags_EnableGamepad; // Setup style ImGui::StyleColorsClassic(); From ed088b00bec24ab89a41a8ec0d6cff50f50244d6 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 25 Jan 2018 18:50:30 +0100 Subject: [PATCH 252/319] Nav: Style: Improved display of windowing highlight (for focus selection via PadMenu or Ctrl+TAB). (#787, #707) --- imgui.cpp | 4 ++-- imgui_draw.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c0ccf1f7..8d73ded9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5710,8 +5710,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { ImRect bb = window->Rect(); bb.Expand(g.FontSize); - window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingDisplayAlpha), g.Style.WindowRounding); - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_HeaderHovered, g.NavWindowingDisplayAlpha), g.Style.WindowRounding); + window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingDisplayAlpha * 0.25f), g.Style.WindowRounding); + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingDisplayAlpha), g.Style.WindowRounding, ~0, 3.0f); } // Draw window + handle manual resize diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 1d21db46..106ad586 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -176,7 +176,7 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered]; - colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.12f); + colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); } void ImGui::StyleColorsDark(ImGuiStyle* dst) @@ -228,7 +228,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.12f); + colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); } // Those light colors are better suited with a thicker font than the default one + FrameBorder @@ -283,7 +283,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered]; - colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.40f, 0.40f, 0.40f, 0.12f); + colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f); } //----------------------------------------------------------------------------- From f2d530040893d9ac91fcd629aa62285351f5c99d Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 25 Jan 2018 19:03:47 +0100 Subject: [PATCH 253/319] Nav: Keyboard: Added CTRL+TAB (and CTRL+Shift+TAB) style window selection. (#787) --- TODO.txt | 1 - imgui.cpp | 119 +++++++++++++++++++++++++++++------------------ imgui.h | 4 +- imgui_internal.h | 7 ++- 4 files changed, 80 insertions(+), 51 deletions(-) diff --git a/TODO.txt b/TODO.txt index a9bc641e..c21d68bb 100644 --- a/TODO.txt +++ b/TODO.txt @@ -237,7 +237,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame) - focus: unable to use SetKeyboardFocusHere() on clipped widgets. (#787) - inputs: rework IO system to be able to pass actual ordered/timestamped events. use an event queue? (~#335, #71) - - inputs: allow to pass explicit double-clicks if that's the only thing the user's backend can get them. (e.g. for windows by the CS_DBLCLKS style). - inputs: support track pad style scrolling & slider edit. - misc: idle refresh: expose cursor blink animation timer for backend to be able to lower framerate. diff --git a/imgui.cpp b/imgui.cpp index 8d73ded9..9a58a65e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2700,42 +2700,52 @@ ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInput return delta; } +static void NavUpdateWindowingHighlightWindow(int focus_change_dir) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.NavWindowingTarget); + if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) + return; + + const int i_current = FindWindowIndex(g.NavWindowingTarget); + ImGuiWindow* window_target = FindWindowNavigable(i_current + focus_change_dir, -INT_MAX, focus_change_dir); + if (!window_target) + window_target = FindWindowNavigable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir); + g.NavWindowingTarget = window_target; + g.NavWindowingToggleLayer = false; +} + // Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer) static void ImGui::NavUpdateWindowing() { ImGuiContext& g = *GImGui; - bool toggle_layer = false; + ImGuiWindow* apply_focus_window = NULL; + bool apply_toggle_layer = false; - if (!g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiInputReadMode_Pressed)) - { - ImGuiWindow* window = g.NavWindow; - if (!window) - window = FindWindowNavigable(g.Windows.Size - 1, -1, -1); - if (window) + bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiInputReadMode_Pressed); + bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard); + if (start_windowing_with_gamepad || start_windowing_with_keyboard) + if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavigable(g.Windows.Size - 1, -INT_MAX, -1)) { g.NavWindowingTarget = window->RootNonPopupWindow; - g.NavWindowingDisplayAlpha = 0.0f; - g.NavWindowingToggleLayer = true; + g.NavWindowingHighlightTimer = g.NavWindowingHighlightAlpha = 0.0f; + g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; + g.NavWindowingIsKeyboardMode = start_windowing_with_keyboard; } - } - if (g.NavWindowingTarget) + + // Gamepad update + g.NavWindowingHighlightTimer += g.IO.DeltaTime; + if (g.NavWindowingTarget && !g.NavWindowingIsKeyboardMode) { - // Visuals only appears after a brief time holding the button, so that a fast tap (to toggle NavLayer) doesn't add visual noise - const float pressed_duration = g.IO.NavInputsDownDuration[ImGuiNavInput_PadMenu]; - g.NavWindowingDisplayAlpha = ImMax(g.NavWindowingDisplayAlpha, ImSaturate((pressed_duration - 0.20f) / 0.05f)); - g.NavWindowingToggleLayer &= (g.NavWindowingDisplayAlpha < 1.0f); // Once button is held long enough we don't consider it a tag-to-toggle-layer press anymore. + // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise + g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.20f) / 0.05f)); // Select window to focus const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_PadFocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_PadFocusNext, ImGuiInputReadMode_RepeatSlow); - if (focus_change_dir != 0 && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)) + if (focus_change_dir != 0) { - const int i_current = FindWindowIndex(g.NavWindowingTarget); - ImGuiWindow* window_target = FindWindowNavigable(i_current + focus_change_dir, -1, focus_change_dir); - if (!window_target) - window_target = FindWindowNavigable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir); - g.NavWindowingTarget = window_target; - g.NavWindowingToggleLayer = false; - g.NavWindowingDisplayAlpha = 1.0f; + NavUpdateWindowingHighlightWindow(focus_change_dir); + g.NavWindowingHighlightAlpha = 1.0f; } // Move window @@ -2751,34 +2761,51 @@ static void ImGui::NavUpdateWindowing() } } + // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most) if (!IsNavInputDown(ImGuiNavInput_PadMenu)) { - // Apply actual focus only when releasing the NavMenu button (until then the window was merely rendered front-most) - if (g.NavWindowingTarget && !g.NavWindowingToggleLayer && (!g.NavWindow || g.NavWindowingTarget != g.NavWindow->RootNonPopupWindow)) - { - FocusWindow(g.NavWindowingTarget); - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - if (g.NavWindowingTarget->NavLastIds[0] == 0) - NavInitWindow(g.NavWindowingTarget, false); - - // If the window only has a menu layer, select it directly - if (g.NavWindowingTarget->DC.NavLayerActiveMask == 0x02) - g.NavLayer = 1; - } - - // Single press toggles NavLayer + g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore. if (g.NavWindowingToggleLayer && g.NavWindow) - toggle_layer = true; + apply_toggle_layer = true; + else if (!g.NavWindowingToggleLayer) + apply_focus_window = g.NavWindowingTarget; g.NavWindowingTarget = NULL; } } - - // Keyboard: Press and release ALT to toggle menu - if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu, ImGuiInputReadMode_Released)) - toggle_layer = true; - if (toggle_layer && g.NavWindow) + // Keyboard: Focus + if (g.NavWindowingTarget && g.NavWindowingIsKeyboardMode) + { + // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise + g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.15f) / 0.04f)); // 1.0f + if (IsKeyPressedMap(ImGuiKey_Tab, true)) + NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1); + if (!g.IO.KeyCtrl) + apply_focus_window = g.NavWindowingTarget; + } + + // Keyboard: Press and release ALT to toggle menu layer + if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu, ImGuiInputReadMode_Released)) + apply_toggle_layer = true; + + // Apply final focus + if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootNonPopupWindow)) + { + g.NavDisableHighlight = false; + g.NavDisableMouseHover = true; + FocusWindow(apply_focus_window); + if (apply_focus_window->NavLastIds[0] == 0) + NavInitWindow(apply_focus_window, false); + + // If the window only has a menu layer, select it directly + if (apply_focus_window->DC.NavLayerActiveMask == (1 << 1)) + g.NavLayer = 1; + } + if (apply_focus_window) + g.NavWindowingTarget = NULL; + + // Apply menu/layer toggle + if (apply_toggle_layer && g.NavWindow) { if ((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) == 0 && (g.NavWindow->RootWindow->DC.NavLayerActiveMask & (1 << 1)) != 0) FocusWindow(g.NavWindow->RootWindow); @@ -5710,8 +5737,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { ImRect bb = window->Rect(); bb.Expand(g.FontSize); - window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingDisplayAlpha * 0.25f), g.Style.WindowRounding); - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingDisplayAlpha), g.Style.WindowRounding, ~0, 3.0f); + window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), g.Style.WindowRounding, ~0, 3.0f); } // Draw window + handle manual resize diff --git a/imgui.h b/imgui.h index 0f6b832d..9d3a5690 100644 --- a/imgui.h +++ b/imgui.h @@ -730,8 +730,8 @@ enum ImGuiNavInput_ // [BETA] Gamepad/Keyboard directional navigation options enum ImGuiNavFlags_ { - ImGuiNavFlags_EnableGamepad = 1 << 0, // Master gamepad navigation enable flag. This is to instruct your imgui binding whether to fill in gamepad navigation inputs. imgui itself won't use this flag. - ImGuiNavFlags_EnableKeyboard = 1 << 1, // Master keyboard navigation enable flag. This is to instruct your imgui binding whether to fill in keyboard navigation inputs. imgui itself won't use this flag. + ImGuiNavFlags_EnableGamepad = 1 << 0, // Master gamepad navigation enable flag. This is mostly to instruct your imgui binding whether to fill in gamepad navigation inputs. + ImGuiNavFlags_EnableKeyboard = 1 << 1, // Master keyboard navigation enable flag. This is mostly to instruct your imgui binding whether to fill in keyboard navigation inputs. ImGuiNavFlags_MoveMouse = 1 << 2 // Request navigation to allow move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantMoveMouse=true. If enabled you MUST honor io.WantMoveMouse requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. }; diff --git a/imgui_internal.h b/imgui_internal.h index a3def317..2d5248e4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -591,8 +591,10 @@ struct ImGuiContext ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest) ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring. ImGuiWindow* NavWindowingTarget; // When selecting a window (holding Menu+FocusPrev/Next, or equivalent of CTRL-TAB) this window is temporarily displayed front-most. - float NavWindowingDisplayAlpha; + float NavWindowingHighlightTimer; + float NavWindowingHighlightAlpha; bool NavWindowingToggleLayer; + bool NavWindowingIsKeyboardMode; // Gamepad or keyboard mode int NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid @@ -710,8 +712,9 @@ struct ImGuiContext NavJustTabbedId = NavJustMovedToId = NavNextActivateId = 0; NavScoringRectScreen = ImRect(); NavWindowingTarget = NULL; - NavWindowingDisplayAlpha = 0.0f; + NavWindowingHighlightTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; + NavWindowingIsKeyboardMode = false; NavLayer = 0; NavIdTabCounter = INT_MAX; NavIdIsAlive = false; From d088bd86adf9eb8816a9ce1ca6e3cc1909de3a9b Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 25 Jan 2018 19:23:13 +0100 Subject: [PATCH 254/319] Nav: Added keyboard moving and resizing via the CTRL-TAB windowing mode. (#787) --- imgui.cpp | 52 +++++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9a58a65e..d47e585d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -27,7 +27,7 @@ - ISSUES & TODO LIST - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS - How can I help? - - How can I dipslay an image? What is ImTextureID, how does it works? + - How can I display an image? What is ImTextureID, how does it works? - How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on labels and the ID stack. - How can I tell when Dear ImGui wants my mouse/keyboard inputs VS when I can pass them to my application? - How can I load a different font than the default? @@ -2748,19 +2748,6 @@ static void ImGui::NavUpdateWindowing() g.NavWindowingHighlightAlpha = 1.0f; } - // Move window - if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) - { - const ImVec2 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadRStick, ImGuiInputReadMode_Down); - 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; - g.NavDisableMouseHover = true; - MarkIniSettingsDirty(g.NavWindowingTarget); - } - } - // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most) if (!IsNavInputDown(ImGuiNavInput_PadMenu)) { @@ -2788,6 +2775,24 @@ static void ImGui::NavUpdateWindowing() if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu, ImGuiInputReadMode_Released)) apply_toggle_layer = true; + // Move window + if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) + { + ImVec2 move_delta; + if (g.NavWindowingIsKeyboardMode && !g.IO.KeyShift) + move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Key, ImGuiInputReadMode_Down); + if (!g.NavWindowingIsKeyboardMode) + move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadRStick, ImGuiInputReadMode_Down); + if (move_delta.x != 0.0f || move_delta.y != 0.0f) + { + const float NAV_MOVE_SPEED = 800.0f; + const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); + g.NavWindowingTarget->PosFloat += move_delta * move_speed; + g.NavDisableMouseHover = true; + MarkIniSettingsDirty(g.NavWindowingTarget); + } + } + // Apply final focus if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootNonPopupWindow)) { @@ -5351,19 +5356,20 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au // Navigation/gamepad resize if (g.NavWindowingTarget == window) { - ImVec2 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); + ImVec2 nav_resize_delta; + if (g.NavWindowingIsKeyboardMode && g.IO.KeyShift) + nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Key, ImGuiInputReadMode_Down); + if (!g.NavWindowingIsKeyboardMode) + nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) { - const float GAMEPAD_RESIZE_SPEED = 600.0f; - nav_resize_delta *= ImFloor(GAMEPAD_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); + const float NAV_RESIZE_SPEED = 600.0f; + nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); + g.NavWindowingToggleLayer = false; g.NavDisableMouseHover = true; resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive); - if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) - { - // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck. - g.NavWindowingToggleLayer = false; - size_target = CalcSizeAfterConstraint(window, window->SizeFull + nav_resize_delta); - } + // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck. + size_target = CalcSizeAfterConstraint(window, window->SizeFull + nav_resize_delta); } } From 04d5783ffd7a445ae2f08dca9c31d698a63c7400 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 25 Jan 2018 19:34:08 +0100 Subject: [PATCH 255/319] Nav: Cleaning up + using ImGuiInputSource source enum instead of a silly bool. (#787) --- imgui.cpp | 36 ++++++++++++++++++------------------ imgui_internal.h | 12 +++++++----- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d47e585d..0301541f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2687,11 +2687,11 @@ static bool IsNavInputPressedAnyOfTwo(ImGuiNavInput n1, ImGuiNavInput n2, ImGuiI ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor) { ImVec2 delta(0.0f, 0.0f); - if (dir_sources & ImGuiNavDirSourceFlags_Key) + if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft, mode), GetNavInputAmount(ImGuiNavInput_KeyDown, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp, mode)); - if (dir_sources & ImGuiNavDirSourceFlags_PadLStick) + if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) delta += ImVec2(GetNavInputAmount(ImGuiNavInput_PadDpadRight, mode) - GetNavInputAmount(ImGuiNavInput_PadDpadLeft, mode), GetNavInputAmount(ImGuiNavInput_PadDpadDown, mode) - GetNavInputAmount(ImGuiNavInput_PadDpadUp, mode)); - if (dir_sources & ImGuiNavDirSourceFlags_PadRStick) + if (dir_sources & ImGuiNavDirSourceFlags_PadLStick) delta += ImVec2(GetNavInputAmount(ImGuiNavInput_PadLStickRight, mode) - GetNavInputAmount(ImGuiNavInput_PadLStickLeft, mode), GetNavInputAmount(ImGuiNavInput_PadLStickDown, mode) - GetNavInputAmount(ImGuiNavInput_PadLStickUp, mode)); if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_PadTweakSlow)) delta *= slow_factor; @@ -2730,12 +2730,12 @@ static void ImGui::NavUpdateWindowing() g.NavWindowingTarget = window->RootNonPopupWindow; g.NavWindowingHighlightTimer = g.NavWindowingHighlightAlpha = 0.0f; g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; - g.NavWindowingIsKeyboardMode = start_windowing_with_keyboard; + g.NavWindowingInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad; } // Gamepad update g.NavWindowingHighlightTimer += g.IO.DeltaTime; - if (g.NavWindowingTarget && !g.NavWindowingIsKeyboardMode) + if (g.NavWindowingTarget && g.NavWindowingInputSource == ImGuiInputSource_NavGamepad) { // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.20f) / 0.05f)); @@ -2761,7 +2761,7 @@ static void ImGui::NavUpdateWindowing() } // Keyboard: Focus - if (g.NavWindowingTarget && g.NavWindowingIsKeyboardMode) + if (g.NavWindowingTarget && g.NavWindowingInputSource == ImGuiInputSource_NavKeyboard) { // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.15f) / 0.04f)); // 1.0f @@ -2779,10 +2779,10 @@ static void ImGui::NavUpdateWindowing() if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) { ImVec2 move_delta; - if (g.NavWindowingIsKeyboardMode && !g.IO.KeyShift) - move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Key, ImGuiInputReadMode_Down); - if (!g.NavWindowingIsKeyboardMode) - move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadRStick, ImGuiInputReadMode_Down); + if (g.NavWindowingInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift) + move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); + if (g.NavWindowingInputSource == ImGuiInputSource_NavGamepad) + move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); if (move_delta.x != 0.0f || move_delta.y != 0.0f) { const float NAV_MOVE_SPEED = 800.0f; @@ -3043,7 +3043,7 @@ static void ImGui::NavUpdate() // *Normal* Manual scroll with NavScrollXXX keys // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. - ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadRStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f); + ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f); if (scroll_dir.x != 0.0f && window->ScrollbarX) { SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); @@ -5357,10 +5357,10 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au if (g.NavWindowingTarget == window) { ImVec2 nav_resize_delta; - if (g.NavWindowingIsKeyboardMode && g.IO.KeyShift) - nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Key, ImGuiInputReadMode_Down); - if (!g.NavWindowingIsKeyboardMode) - nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); + if (g.NavWindowingInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift) + nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); + if (g.NavWindowingInputSource == ImGuiInputSource_NavGamepad) + nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down); if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) { const float NAV_RESIZE_SPEED = 600.0f; @@ -8401,7 +8401,7 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v } else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivateDownId == id) { - const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Key|ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f); + const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard|ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f); float delta = is_horizontal ? delta2.x : -delta2.y; if (delta != 0.0f) { @@ -8748,7 +8748,7 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s } if (g.ActiveIdSource == ImGuiInputSource_Nav) { - adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Key|ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; + adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard|ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); } adjust_delta *= v_speed; @@ -12981,7 +12981,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) } if (ImGui::TreeNode("Basic state")) { - const char* input_source_names[] = { "None", "Mouse", "Nav" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_Count_); + const char* input_source_names[] = { "None", "Mouse", "Nav", "NavGamepad", "NavKeyboard" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_Count_); ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec)", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not diff --git a/imgui_internal.h b/imgui_internal.h index 2d5248e4..3b1923d1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -252,6 +252,8 @@ enum ImGuiInputSource ImGuiInputSource_None = 0, ImGuiInputSource_Mouse, ImGuiInputSource_Nav, + ImGuiInputSource_NavKeyboard, // Only used occasionally for storage, not tested/handled by most code + ImGuiInputSource_NavGamepad, // " ImGuiInputSource_Count_, }; @@ -285,9 +287,9 @@ enum ImGuiNavHighlightFlags_ enum ImGuiNavDirSourceFlags_ { - ImGuiNavDirSourceFlags_Key = 1 << 0, - ImGuiNavDirSourceFlags_PadLStick = 1 << 1, - ImGuiNavDirSourceFlags_PadRStick = 1 << 2 + ImGuiNavDirSourceFlags_Keyboard = 1 << 0, + ImGuiNavDirSourceFlags_PadDPad = 1 << 1, + ImGuiNavDirSourceFlags_PadLStick = 1 << 2 }; // 2D axis aligned bounding-box @@ -594,7 +596,7 @@ struct ImGuiContext float NavWindowingHighlightTimer; float NavWindowingHighlightAlpha; bool NavWindowingToggleLayer; - bool NavWindowingIsKeyboardMode; // Gamepad or keyboard mode + ImGuiInputSource NavWindowingInputSource; // Gamepad or keyboard mode int NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid @@ -714,7 +716,7 @@ struct ImGuiContext NavWindowingTarget = NULL; NavWindowingHighlightTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; - NavWindowingIsKeyboardMode = false; + NavWindowingInputSource = ImGuiInputSource_None; NavLayer = 0; NavIdTabCounter = INT_MAX; NavIdIsAlive = false; From 10a4a77b27d598bf10fb617e4180488eaf0adc2a Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 25 Jan 2018 20:39:34 +0100 Subject: [PATCH 256/319] Nav: Drag, Slider: When already past a limit and pushing in the direction of the limit, we don't clamp values again. (#787) --- imgui.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0301541f..a5f4dee6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2939,7 +2939,7 @@ static void ImGui::NavUpdate() ImGuiWindow* parent_window = g.NavWindow->ParentWindow; FocusWindow(parent_window); IM_ASSERT(child_window->ChildId != 0); - SetNavID(child_window->ChildId, g.NavLayer); // FIXME-NAV: Layer not necessarily correct + SetNavID(child_window->ChildId, 0); g.NavIdIsAlive = false; if (g.NavDisableMouseHover) g.NavMousePosDirty = true; @@ -8421,8 +8421,10 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v } if (IsNavInputDown(ImGuiNavInput_PadTweakFast)) delta *= 10.0f; - clicked_t = ImSaturate(clicked_t + delta); // FIXME-NAV: todo: cancel adjustment if current value already past edge and we are moving in edge direction, to avoid clamping value to edge. + clicked_t = ImSaturate(clicked_t + delta); set_new_value = true; + if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits + set_new_value = false; } } else @@ -8749,6 +8751,8 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s if (g.ActiveIdSource == ImGuiInputSource_Nav) { adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard|ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; + if (v_min < v_max && ((v_cur >= v_max && adjust_delta > 0.0f) || (v_cur <= v_min && adjust_delta < 0.0f))) // This is to avoid applying the saturation when already past the limits + adjust_delta = 0.0f; v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); } adjust_delta *= v_speed; From 21771adb94c92a0d73d4dd42b42d8d6e4bf9ab80 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 29 Jan 2018 15:35:51 +0100 Subject: [PATCH 257/319] Nav: Debug overlay uses default font. (#787) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index c8add8ce..d4c7835e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2198,7 +2198,7 @@ static bool NavScoreItem(ImRect cand) ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f) dcen (%.2f,%.2f->%.4f) d (%.2f,%.2f->%.4f) nav %c, quad %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); g.OverlayDrawList.AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); g.OverlayDrawList.AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150)); - g.OverlayDrawList.AddText(cand.Max, ~0U, buf); + g.OverlayDrawList.AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); } #endif From c8b9b2c6bdc7710221d163dd49a7a787cba7a483 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 29 Jan 2018 15:46:41 +0100 Subject: [PATCH 258/319] Nav: Mouse hovering selectable / menu items only activate them if they are on the active NavLayer. (#787) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index d4c7835e..7a4ee902 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10600,7 +10600,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets) if (pressed || hovered)// && (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)) - if (!g.NavDisableMouseHover && g.NavWindow == window) + if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerActiveMask) { g.NavDisableHighlight = true; SetNavID(id, window->DC.NavLayerCurrent); From 4654040bcbe58bd55af5512297524afb71ddfc79 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 29 Jan 2018 17:35:36 +0100 Subject: [PATCH 259/319] Nav: Comments, added enum to clarify NavForward code. (#787) --- imgui.cpp | 19 ++++++++++--------- imgui_internal.h | 13 ++++++++++--- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7a4ee902..9c7e4a83 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2896,12 +2896,12 @@ static void ImGui::NavUpdate() } // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame - if (g.NavMoveRequestForwardStep == 2) + if (g.NavMoveRequestForward == ImGuiNavForward_ForwardResult) { IM_ASSERT(g.NavMoveRequest); if (g.NavMoveResultId == 0) g.NavDisableHighlight = false; - g.NavMoveRequestForwardStep = 0; + g.NavMoveRequestForward = ImGuiNavForward_None; } // Apply application mouse position movement, after we had a chance to process move request result. @@ -2993,7 +2993,7 @@ static void ImGui::NavUpdate() // Initiate directional inputs request const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; - if (g.NavMoveRequestForwardStep == 0) + if (g.NavMoveRequestForward == ImGuiNavForward_None) { g.NavMoveDir = ImGuiDir_None; if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) @@ -3006,9 +3006,10 @@ static void ImGui::NavUpdate() } else { + // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window) IM_ASSERT(g.NavMoveDir != ImGuiDir_None); - IM_ASSERT(g.NavMoveRequestForwardStep == 1); - g.NavMoveRequestForwardStep = 2; + IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_Forwarding); + g.NavMoveRequestForward = ImGuiNavForward_ForwardResult; } if (g.NavMoveDir != ImGuiDir_None) @@ -4821,9 +4822,9 @@ static void NavProcessMoveRequestWrapAround(ImGuiWindow* window) { ImGuiContext& g = *GImGui; if (g.NavMoveRequest && g.NavWindow == window && g.NavMoveResultId == 0) - if ((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) && g.NavMoveRequestForwardStep == 0 && g.NavLayer == 0) + if ((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == 0) { - g.NavMoveRequestForwardStep = 1; + g.NavMoveRequestForward = ImGuiNavForward_Forwarding; NavMoveRequestCancel(); g.NavWindow->NavRectRel[0].Min.y = g.NavWindow->NavRectRel[0].Max.y = ((g.NavMoveDir == ImGuiDir_Up) ? ImMax(window->SizeFull.y, window->SizeContents.y) : 0.0f) - window->Scroll.y; } @@ -10860,7 +10861,7 @@ void ImGui::EndMenuBar() ImGuiWindow* nav_earliest_child = g.NavWindow; while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) nav_earliest_child = nav_earliest_child->ParentWindow; - if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForwardStep == 0) + if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None) { // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost) @@ -10869,7 +10870,7 @@ void ImGui::EndMenuBar() SetNavIDAndMoveMouse(window->NavLastIds[1], 1, window->NavRectRel[1]); g.NavLayer = 1; g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. - g.NavMoveRequestForwardStep = 1; + g.NavMoveRequestForward = ImGuiNavForward_Forwarding; NavMoveRequestCancel(); } } diff --git a/imgui_internal.h b/imgui_internal.h index c958f33e..75cabcf6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -292,6 +292,13 @@ enum ImGuiNavDirSourceFlags_ ImGuiNavDirSourceFlags_PadLStick = 1 << 2 }; +enum ImGuiNavForward +{ + ImGuiNavForward_None, + ImGuiNavForward_Forwarding, + ImGuiNavForward_ForwardResult +}; + // 2D axis aligned bounding-box // NB: we can't rely on ImVec2 math operators being available here struct IMGUI_API ImRect @@ -610,11 +617,11 @@ struct ImGuiContext bool NavInitResultExplicit; // Whether the result was explicitly requested with SetItemDefaultFocus() bool NavMoveFromClampedRefRect; // Set by manual scrolling, if we scroll to a point where NavId isn't visible we reset navigation from visible items bool NavMoveRequest; // Move request for this frame - int NavMoveRequestForwardStep; // 0: no forward, 1: forward request, 2: forward result (this is used to navigate sibling parent menus from a child menu) + ImGuiNavForward NavMoveRequestForward; // No forward / Forwarding / ForwardResult (this is used to navigate sibling parent menus from a child menu) ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down) ImGuiDir NavMoveDirLast; // Direction of the previous move request ImGuiID NavMoveResultId; // Best move request candidate - ImGuiID NavMoveResultParentId; // + ImGuiID NavMoveResultParentId; // Best move request candidate window->IDStack.back() - to compare context float NavMoveResultDistBox; // Best move request candidate box distance to current NavId float NavMoveResultDistCenter; // Best move request candidate center distance to current NavId float NavMoveResultDistAxial; @@ -729,7 +736,7 @@ struct ImGuiContext NavInitResultExplicit = false; NavMoveFromClampedRefRect = false; NavMoveRequest = false; - NavMoveRequestForwardStep = 0; + NavMoveRequestForward = ImGuiNavForward_None; NavMoveDir = NavMoveDirLast = ImGuiDir_None; NavMoveResultId = 0; NavMoveResultParentId = 0; From 1cf38d0334c4837ba7ab9d9b6e5c6e215a146871 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 29 Jan 2018 18:12:32 +0100 Subject: [PATCH 260/319] Internals: Nav: Renamed ImGuiNavForward enum (#787) --- imgui.cpp | 10 +++++----- imgui_internal.h | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9c7e4a83..e2f515f8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2896,7 +2896,7 @@ static void ImGui::NavUpdate() } // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame - if (g.NavMoveRequestForward == ImGuiNavForward_ForwardResult) + if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) { IM_ASSERT(g.NavMoveRequest); if (g.NavMoveResultId == 0) @@ -3008,8 +3008,8 @@ static void ImGui::NavUpdate() { // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window) IM_ASSERT(g.NavMoveDir != ImGuiDir_None); - IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_Forwarding); - g.NavMoveRequestForward = ImGuiNavForward_ForwardResult; + IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued); + g.NavMoveRequestForward = ImGuiNavForward_ForwardActive; } if (g.NavMoveDir != ImGuiDir_None) @@ -4824,7 +4824,7 @@ static void NavProcessMoveRequestWrapAround(ImGuiWindow* window) if (g.NavMoveRequest && g.NavWindow == window && g.NavMoveResultId == 0) if ((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == 0) { - g.NavMoveRequestForward = ImGuiNavForward_Forwarding; + g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; NavMoveRequestCancel(); g.NavWindow->NavRectRel[0].Min.y = g.NavWindow->NavRectRel[0].Max.y = ((g.NavMoveDir == ImGuiDir_Up) ? ImMax(window->SizeFull.y, window->SizeContents.y) : 0.0f) - window->Scroll.y; } @@ -10870,7 +10870,7 @@ void ImGui::EndMenuBar() SetNavIDAndMoveMouse(window->NavLastIds[1], 1, window->NavRectRel[1]); g.NavLayer = 1; g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. - g.NavMoveRequestForward = ImGuiNavForward_Forwarding; + g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; NavMoveRequestCancel(); } } diff --git a/imgui_internal.h b/imgui_internal.h index 75cabcf6..2713aa56 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -295,8 +295,8 @@ enum ImGuiNavDirSourceFlags_ enum ImGuiNavForward { ImGuiNavForward_None, - ImGuiNavForward_Forwarding, - ImGuiNavForward_ForwardResult + ImGuiNavForward_ForwardQueued, + ImGuiNavForward_ForwardActive }; // 2D axis aligned bounding-box @@ -617,7 +617,7 @@ struct ImGuiContext bool NavInitResultExplicit; // Whether the result was explicitly requested with SetItemDefaultFocus() bool NavMoveFromClampedRefRect; // Set by manual scrolling, if we scroll to a point where NavId isn't visible we reset navigation from visible items bool NavMoveRequest; // Move request for this frame - ImGuiNavForward NavMoveRequestForward; // No forward / Forwarding / ForwardResult (this is used to navigate sibling parent menus from a child menu) + ImGuiNavForward NavMoveRequestForward; // None / ForwardQueued / ForwardActive (this is used to navigate sibling parent menus from a child menu) ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down) ImGuiDir NavMoveDirLast; // Direction of the previous move request ImGuiID NavMoveResultId; // Best move request candidate From 72485a5d0443e0ed0c8464f04a44a3d832a402b8 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 29 Jan 2018 18:28:46 +0100 Subject: [PATCH 261/319] Nav: Refactor NavMoveResult** flags into ImGuiNavMoveResult structure as we are going to want two instances of it. (#787) (+1 squashed commits) + store window inside result. --- imgui.cpp | 58 ++++++++++++++++++++++++++---------------------- imgui_internal.h | 23 +++++++++++-------- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e2f515f8..cfa2d8e7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2132,7 +2132,7 @@ static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1) } // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057 -static bool NavScoreItem(ImRect cand) +static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.NavWindow; @@ -2207,21 +2207,21 @@ static bool NavScoreItem(ImRect cand) if (quadrant == g.NavMoveDir) { // Does it beat the current best candidate? - if (dist_box < g.NavMoveResultDistBox) + if (dist_box < result->DistBox) { - g.NavMoveResultDistBox = dist_box; - g.NavMoveResultDistCenter = dist_center; + result->DistBox = dist_box; + result->DistCenter = dist_center; return true; } - if (dist_box == g.NavMoveResultDistBox) + if (dist_box == result->DistBox) { // Try using distance between center points to break ties - if (dist_center < g.NavMoveResultDistCenter) + if (dist_center < result->DistCenter) { - g.NavMoveResultDistCenter = dist_center; + result->DistCenter = dist_center; new_best = true; } - else if (dist_center == g.NavMoveResultDistCenter) + else if (dist_center == result->DistCenter) { // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), @@ -2237,11 +2237,11 @@ static bool NavScoreItem(ImRect cand) // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too. // 2017/09/29: FIXME: This now currently only enabled inside menubars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward. // Disabling it may however lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? - if (g.NavMoveResultDistBox == FLT_MAX && dist_axial < g.NavMoveResultDistAxial) // Check axial match + if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f)) { - g.NavMoveResultDistAxial = dist_axial; + result->DistAxial = dist_axial; new_best = true; } @@ -2288,19 +2288,21 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con // Scoring for navigation if (g.NavId != id && !(item_flags & ImGuiItemFlags_NoNav)) { + ImGuiNavMoveResult* result = &g.NavMoveResult; #if IMGUI_DEBUG_NAV_SCORING // [DEBUG] Score all items in NavWindow at all times if (!g.NavMoveRequest) g.NavMoveDir = g.NavMoveDirLast; - bool new_best = NavScoreItem(nav_bb) && g.NavMoveRequest; + bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest; #else - bool new_best = g.NavMoveRequest && NavScoreItem(nav_bb); + bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb); #endif if (new_best) { - g.NavMoveResultId = id; - g.NavMoveResultParentId = window->IDStack.back(); - g.NavMoveResultRectRel = nav_bb_rel; + result->ID = id; + result->ParentID = window->IDStack.back(); + result->Window = window; + result->RectRel = nav_bb_rel; } } @@ -2881,17 +2883,18 @@ static void ImGui::NavUpdate() g.NavJustMovedToId = 0; // Process navigation move request - if (g.NavMoveRequest && g.NavMoveResultId != 0) + if (g.NavMoveRequest && g.NavMoveResult.ID != 0) { // Scroll to keep newly navigated item fully into view - IM_ASSERT(g.NavWindow); + ImGuiNavMoveResult* result = &g.NavMoveResult; + IM_ASSERT(g.NavWindow && result->Window); if (g.NavLayer == 0) - NavScrollToBringItemIntoView(g.NavWindow, g.NavMoveResultRectRel); + NavScrollToBringItemIntoView(result->Window, result->RectRel); // Apply result from previous frame navigation directional move request ClearActiveID(); - SetNavIDAndMoveMouse(g.NavMoveResultId, g.NavLayer, g.NavMoveResultRectRel); - g.NavJustMovedToId = g.NavMoveResultId; + SetNavIDAndMoveMouse(result->ID, g.NavLayer, result->RectRel); + g.NavJustMovedToId = result->ID; g.NavMoveFromClampedRefRect = false; } @@ -2899,7 +2902,7 @@ static void ImGui::NavUpdate() if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) { IM_ASSERT(g.NavMoveRequest); - if (g.NavMoveResultId == 0) + if (g.NavMoveResult.ID == 0) g.NavDisableHighlight = false; g.NavMoveRequestForward = ImGuiNavForward_None; } @@ -3058,9 +3061,10 @@ static void ImGui::NavUpdate() } // Reset search - g.NavMoveResultId = 0; - g.NavMoveResultParentId = 0; - g.NavMoveResultDistAxial = g.NavMoveResultDistBox = g.NavMoveResultDistCenter = FLT_MAX; + ImGuiNavMoveResult* result = &g.NavMoveResult; + result->ID = result->ParentID = 0; + result->Window = NULL; + result->DistAxial = result->DistBox = result->DistCenter = FLT_MAX; // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0) @@ -4821,7 +4825,7 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla static void NavProcessMoveRequestWrapAround(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - if (g.NavMoveRequest && g.NavWindow == window && g.NavMoveResultId == 0) + if (g.NavMoveRequest && g.NavWindow == window && g.NavMoveResult.ID == 0) if ((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == 0) { g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; @@ -10856,7 +10860,7 @@ void ImGui::EndMenuBar() ImGuiContext& g = *GImGui; // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. - if (g.NavMoveRequest && g.NavMoveResultId == 0 && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) + if (g.NavMoveRequest && g.NavMoveResult.ID == 0 && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) { ImGuiWindow* nav_earliest_child = g.NavWindow; while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) @@ -11024,7 +11028,7 @@ void ImGui::EndMenu() // Nav: When a left move request within our child menu failed, close the menu ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveRequest && g.NavMoveResultId == 0 && g.NavMoveDir == ImGuiDir_Left && window->DC.LayoutType == ImGuiLayoutType_Vertical) + if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveRequest && g.NavMoveResult.ID == 0 && g.NavMoveDir == ImGuiDir_Left && window->DC.LayoutType == ImGuiLayoutType_Vertical) { ClosePopupToLevel(g.OpenPopupStack.Size - 1); g.NavMoveRequest = false; diff --git a/imgui_internal.h b/imgui_internal.h index 2713aa56..f277882c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -504,6 +504,19 @@ struct ImDrawDataBuilder IMGUI_API void FlattenIntoSingleLayer(); }; +struct ImGuiNavMoveResult +{ + ImGuiID ID; // Best candidate + ImGuiID ParentID; // Best candidate window->IDStack.back() - to compare context + ImGuiWindow* Window; // Best candidate window + float DistBox; // Best candidate box distance to current NavId + float DistCenter; // Best candidate center distance to current NavId + float DistAxial; // Best candidate selected distance (box/center) to current NavId + ImRect RectRel; // Best candidate bounding box in window relative space + + ImGuiNavMoveResult() { ID = ParentID = 0; Window = NULL; DistBox = DistCenter = DistAxial = 0.0f; } +}; + // Storage for SetNexWindow** functions struct ImGuiNextWindowData { @@ -620,12 +633,7 @@ struct ImGuiContext ImGuiNavForward NavMoveRequestForward; // None / ForwardQueued / ForwardActive (this is used to navigate sibling parent menus from a child menu) ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down) ImGuiDir NavMoveDirLast; // Direction of the previous move request - ImGuiID NavMoveResultId; // Best move request candidate - ImGuiID NavMoveResultParentId; // Best move request candidate window->IDStack.back() - to compare context - float NavMoveResultDistBox; // Best move request candidate box distance to current NavId - float NavMoveResultDistCenter; // Best move request candidate center distance to current NavId - float NavMoveResultDistAxial; - ImRect NavMoveResultRectRel; // Best move request candidate bounding box in window relative space + ImGuiNavMoveResult NavMoveResult; // Best move request candidate // Render ImDrawData DrawData; // Main ImDrawData instance to pass render information to the user @@ -738,9 +746,6 @@ struct ImGuiContext NavMoveRequest = false; NavMoveRequestForward = ImGuiNavForward_None; NavMoveDir = NavMoveDirLast = ImGuiDir_None; - NavMoveResultId = 0; - NavMoveResultParentId = 0; - NavMoveResultDistBox = NavMoveResultDistCenter = NavMoveResultDistAxial = 0.0f; ModalWindowDarkeningRatio = 0.0f; OverlayDrawList._Data = &DrawListSharedData; From c8d8dc7f0a99a9e1ca6c03ccff93dc08e6ac8856 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 29 Jan 2018 22:15:55 +0100 Subject: [PATCH 262/319] Nav: Internals: Renamed RootNavWindow to NavRootWindow (#787) --- imgui.cpp | 23 ++++++++++++----------- imgui_internal.h | 10 +++++----- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cfa2d8e7..e3666e88 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1906,9 +1906,6 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); - NavLastIds[0] = NavLastIds[1] = 0; - NavRectRel[0] = NavRectRel[1] = ImRect(); - LastFrameActive = -1; ItemWidthDefault = 0.0f; FontWindowScale = 1.0f; @@ -1918,7 +1915,10 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) ParentWindow = NULL; RootWindow = NULL; RootNonPopupWindow = NULL; - RootNavWindow = NULL; + + NavRootWindow = NULL; + NavLastIds[0] = NavLastIds[1] = 0; + NavRectRel[0] = NavRectRel[1] = ImRect(); FocusIdxAllCounter = FocusIdxTabCounter = -1; FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX; @@ -2235,7 +2235,7 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness) // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too. - // 2017/09/29: FIXME: This now currently only enabled inside menubars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward. + // 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward. // Disabling it may however lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) @@ -2333,7 +2333,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) // it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask; - if (g.NavWindow == window->RootNavWindow) + if (g.NavWindow == window->NavRootWindow) if (g.NavId == id || g.NavAnyRequest) NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id); } @@ -2885,9 +2885,10 @@ static void ImGui::NavUpdate() // Process navigation move request if (g.NavMoveRequest && g.NavMoveResult.ID != 0) { - // Scroll to keep newly navigated item fully into view ImGuiNavMoveResult* result = &g.NavMoveResult; IM_ASSERT(g.NavWindow && result->Window); + + // Scroll to keep newly navigated item fully into view if (g.NavLayer == 0) NavScrollToBringItemIntoView(result->Window, result->RectRel); @@ -5527,9 +5528,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->RootWindow = parent_window->RootWindow; if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) window->RootNonPopupWindow = parent_window->RootNonPopupWindow; - window->RootNavWindow = window; - //while (window->RootNavWindow->Flags & ImGuiWindowFlags_NavFlattened) - // window->RootNavWindow = window->RootNavWindow->ParentWindow; + window->NavRootWindow = window; + //while (window->NavRootWindow->Flags & ImGuiWindowFlags_NavFlattened) + // window->NavRootWindow = window->NavRootWindow->ParentWindow; window->Active = true; window->BeginOrderWithinParent = 0; @@ -7036,7 +7037,7 @@ void ImGui::SetItemDefaultFocus() ImGuiWindow* window = g.CurrentWindow; if (!window->Appearing) return; - if (g.NavWindow == window->RootNavWindow && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent) + if (g.NavWindow == window->NavRootWindow && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent) { g.NavInitRequest = false; g.NavInitResultExplicit = true; diff --git a/imgui_internal.h b/imgui_internal.h index f277882c..7328429f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -511,7 +511,7 @@ struct ImGuiNavMoveResult ImGuiWindow* Window; // Best candidate window float DistBox; // Best candidate box distance to current NavId float DistCenter; // Best candidate center distance to current NavId - float DistAxial; // Best candidate selected distance (box/center) to current NavId + float DistAxial; ImRect RectRel; // Best candidate bounding box in window relative space ImGuiNavMoveResult() { ID = ParentID = 0; Window = NULL; DistBox = DistCenter = DistAxial = 0.0f; } @@ -921,9 +921,6 @@ struct IMGUI_API ImGuiWindow ImVec2 SetWindowPosVal; // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size) ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0,0) when positioning from top-left corner; ImVec2(0.5f,0.5f) for centering; ImVec2(1,1) for bottom right. - ImGuiID NavLastIds[2]; // Last known NavId for this window, per layer (0/1) - ImRect NavRectRel[2]; // Reference rectangle, in window relative space - ImGuiDrawContext DC; // Temporary per-window data, reset at the beginning of the frame ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack ImRect ClipRect; // = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2. @@ -939,7 +936,10 @@ struct IMGUI_API ImGuiWindow ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL. ImGuiWindow* RootWindow; // Generally point to ourself. If we are a child window, this is pointing to the first non-child parent window. ImGuiWindow* RootNonPopupWindow; // Generally point to ourself. Used to display TitleBgActive color and for selecting which window to use for NavWindowing - ImGuiWindow* RootNavWindow; // Generally point to ourself. If we are a child window with the ImGuiWindowFlags_NavFlattenedChild flag, point to parent. Used to display TitleBgActive color and for selecting which window to use for NavWindowing. + + ImGuiWindow* NavRootWindow; // Generally point to ourself. If we are a child window with the NavFlattened flag, point to a parent window. + ImGuiID NavLastIds[2]; // Last known NavId for this window, per layer (0/1) + ImRect NavRectRel[2]; // Reference rectangle, in window relative space // Navigation / Focus // FIXME-NAV: Merge all this with the new Nav system, at least the request variables should be moved to ImGuiContext From 020b153d35667a97c27bdf60baad41ca08a66ddd Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 29 Jan 2018 22:51:28 +0100 Subject: [PATCH 263/319] Nav: calling NavMoveRequestCancel() more consistently when hijacking a request. Not strictly necessary. (#787) --- imgui.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e3666e88..2dba6664 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7839,12 +7839,12 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open) { toggled = true; - g.NavMoveRequest = false; + NavMoveRequestCancel(); } if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? { toggled = true; - g.NavMoveRequest = false; + NavMoveRequestCancel(); } if (toggled) @@ -10976,7 +10976,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open { want_open = true; - g.NavMoveRequest = false; + NavMoveRequestCancel(); } } else @@ -10993,8 +10993,8 @@ bool ImGui::BeginMenu(const char* label, bool enabled) } else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open { - g.NavMoveRequest = false; want_open = true; + NavMoveRequestCancel(); } } @@ -11032,7 +11032,7 @@ void ImGui::EndMenu() if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveRequest && g.NavMoveResult.ID == 0 && g.NavMoveDir == ImGuiDir_Left && window->DC.LayoutType == ImGuiLayoutType_Vertical) { ClosePopupToLevel(g.OpenPopupStack.Size - 1); - g.NavMoveRequest = false; + NavMoveRequestCancel(); } EndPopup(); From b40dc5c4f2935cfdb68c7195745aad0bf8bc19ed Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 29 Jan 2018 22:57:17 +0100 Subject: [PATCH 264/319] Nav: NavScoreItem uses g.CurrentWindow and not g.NavWindow. This was changed as part of b3cba62b80038bd8c8fd722ba5e4e13c83bfb8bc when first trying to implement the NavFlattenedFlag. As it turns out we won't need it. Committing separately for safety. (#787) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 2dba6664..edd56adf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2135,7 +2135,7 @@ static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1) static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.NavWindow; + ImGuiWindow* window = g.CurrentWindow; if (g.NavLayer != window->DC.NavLayerCurrent) return false; From c851b33352b88cd512008f113bc76db363dee3d2 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 29 Jan 2018 23:06:55 +0100 Subject: [PATCH 265/319] Nav: Added proper version of ImGuiWindowFlags_NavFlattened that handles scrolling nicely. Marked as private as I'm not happy with the name. (#787) --- imgui.cpp | 54 +++++++++++++++++++++++++++++------------------- imgui.h | 2 +- imgui_demo.cpp | 2 +- imgui_internal.h | 6 ++++-- 4 files changed, 39 insertions(+), 25 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index edd56adf..965305ba 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2254,6 +2254,12 @@ static inline void NavUpdateAnyRequestFlag() g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || IMGUI_DEBUG_NAV_SCORING; } +static bool NavMoveRequestButNoResultYet() +{ + ImGuiContext& g = *GImGui; + return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; +} + static void NavMoveRequestCancel() { ImGuiContext& g = *GImGui; @@ -2288,7 +2294,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con // Scoring for navigation if (g.NavId != id && !(item_flags & ImGuiItemFlags_NoNav)) { - ImGuiNavMoveResult* result = &g.NavMoveResult; + ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; #if IMGUI_DEBUG_NAV_SCORING // [DEBUG] Score all items in NavWindow at all times if (!g.NavMoveRequest) @@ -2333,9 +2339,10 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) // it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask; - if (g.NavWindow == window->NavRootWindow) - if (g.NavId == id || g.NavAnyRequest) - NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id); + if (g.NavId == id || g.NavAnyRequest) + if (g.NavWindow->NavRootWindow == window->NavRootWindow) + if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) + NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id); } window->DC.LastItemId = id; @@ -2883,9 +2890,14 @@ static void ImGui::NavUpdate() g.NavJustMovedToId = 0; // Process navigation move request - if (g.NavMoveRequest && g.NavMoveResult.ID != 0) + if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0)) { - ImGuiNavMoveResult* result = &g.NavMoveResult; + // Select which result to use + ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; + if (g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) // Maybe entering a flattened child? In this case solve the tie using the regular scoring rules + if ((g.NavMoveResultOther.DistBox < g.NavMoveResultLocal.DistBox) || (g.NavMoveResultOther.DistBox == g.NavMoveResultLocal.DistBox && g.NavMoveResultOther.DistCenter < g.NavMoveResultLocal.DistCenter)) + result = &g.NavMoveResultOther; + IM_ASSERT(g.NavWindow && result->Window); // Scroll to keep newly navigated item fully into view @@ -2894,6 +2906,7 @@ static void ImGui::NavUpdate() // Apply result from previous frame navigation directional move request ClearActiveID(); + g.NavWindow = result->Window; SetNavIDAndMoveMouse(result->ID, g.NavLayer, result->RectRel); g.NavJustMovedToId = result->ID; g.NavMoveFromClampedRefRect = false; @@ -2903,7 +2916,7 @@ static void ImGui::NavUpdate() if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) { IM_ASSERT(g.NavMoveRequest); - if (g.NavMoveResult.ID == 0) + if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) g.NavDisableHighlight = false; g.NavMoveRequestForward = ImGuiNavForward_None; } @@ -3061,11 +3074,9 @@ static void ImGui::NavUpdate() } } - // Reset search - ImGuiNavMoveResult* result = &g.NavMoveResult; - result->ID = result->ParentID = 0; - result->Window = NULL; - result->DistAxial = result->DistBox = result->DistCenter = FLT_MAX; + // Reset search results + g.NavMoveResultLocal.Clear(); + g.NavMoveResultOther.Clear(); // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0) @@ -4826,7 +4837,7 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla static void NavProcessMoveRequestWrapAround(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - if (g.NavMoveRequest && g.NavWindow == window && g.NavMoveResult.ID == 0) + if (g.NavWindow == window && NavMoveRequestButNoResultYet()) if ((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == 0) { g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; @@ -4928,7 +4939,7 @@ static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b g.Style.ChildBorderSize = backup_border_size; // Process navigation-in immediately so NavInit can run on first frame - if (/*!(flags & ImGuiWindowFlags_NavFlattened) &&*/ (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll) && g.NavActivateId == id) + if (!(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll) && g.NavActivateId == id) { ImGui::FocusWindow(child_window); ImGui::NavInitWindow(child_window, false); @@ -4972,7 +4983,7 @@ void ImGui::EndChild() ImGuiWindow* parent_window = GetCurrentWindow(); ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); ItemSize(sz); - if (/*!(window->Flags & ImGuiWindowFlags_NavFlattened) &&*/ (window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll)) + if (!(window->Flags & ImGuiWindowFlags_NavFlattened) && (window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll)) { ItemAdd(bb, window->ChildId); RenderNavHighlight(bb, window->ChildId); @@ -5420,8 +5431,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Automatically disable manual moving/resizing when NoInputs is set if (flags & ImGuiWindowFlags_NoInputs) flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; - //if (flags & ImGuiWindowFlags_NavFlattened) - // IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow); + + if (flags & ImGuiWindowFlags_NavFlattened) + IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow); const int current_frame = g.FrameCount; const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); @@ -5529,8 +5541,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) window->RootNonPopupWindow = parent_window->RootNonPopupWindow; window->NavRootWindow = window; - //while (window->NavRootWindow->Flags & ImGuiWindowFlags_NavFlattened) - // window->NavRootWindow = window->NavRootWindow->ParentWindow; + while (window->NavRootWindow->Flags & ImGuiWindowFlags_NavFlattened) + window->NavRootWindow = window->NavRootWindow->ParentWindow; window->Active = true; window->BeginOrderWithinParent = 0; @@ -10861,7 +10873,7 @@ void ImGui::EndMenuBar() ImGuiContext& g = *GImGui; // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. - if (g.NavMoveRequest && g.NavMoveResult.ID == 0 && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) + if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) { ImGuiWindow* nav_earliest_child = g.NavWindow; while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) @@ -11029,7 +11041,7 @@ void ImGui::EndMenu() // Nav: When a left move request within our child menu failed, close the menu ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveRequest && g.NavMoveResult.ID == 0 && g.NavMoveDir == ImGuiDir_Left && window->DC.LayoutType == ImGuiLayoutType_Vertical) + if (g.NavWindow && g.NavWindow->ParentWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveDir == ImGuiDir_Left && window->DC.LayoutType == ImGuiLayoutType_Vertical) { ClosePopupToLevel(g.OpenPopupStack.Size - 1); NavMoveRequestCancel(); diff --git a/imgui.h b/imgui.h index 24831dba..8659f1d7 100644 --- a/imgui.h +++ b/imgui.h @@ -552,9 +552,9 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // (WIP) Enable resize from any corners and borders. Your back-end needs to honor the different values of io.MouseCursor set by imgui. ImGuiWindowFlags_NoNavFocus = 1 << 18, // No focusing of this window with gamepad/keyboard navigation ImGuiWindowFlags_NoNavInputs = 1 << 19, // No gamepad/keyboard navigation within the window - //ImGuiWindowFlags_NavFlattened = 1 << 20, // Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) // [Internal] + ImGuiWindowFlags_NavFlattened = 1 << 20, // (WIP) Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) ImGuiWindowFlags_ChildWindow = 1 << 24, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index a642e5d1..2746b8a0 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2148,7 +2148,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::RadioButton("Alpha", &alpha_flags, ImGuiColorEditFlags_AlphaPreview); ImGui::SameLine(); ImGui::RadioButton("Both", &alpha_flags, ImGuiColorEditFlags_AlphaPreviewHalf); - ImGui::BeginChild("#colors", ImVec2(0, 300), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); + ImGui::BeginChild("#colors", ImVec2(0, 300), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened); ImGui::PushItemWidth(-160); for (int i = 0; i < ImGuiCol_COUNT; i++) { diff --git a/imgui_internal.h b/imgui_internal.h index 7328429f..7a691cb7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -514,7 +514,8 @@ struct ImGuiNavMoveResult float DistAxial; ImRect RectRel; // Best candidate bounding box in window relative space - ImGuiNavMoveResult() { ID = ParentID = 0; Window = NULL; DistBox = DistCenter = DistAxial = 0.0f; } + ImGuiNavMoveResult() { Clear(); } + void Clear() { ID = ParentID = 0; Window = NULL; DistBox = DistCenter = DistAxial = FLT_MAX; RectRel = ImRect(); } }; // Storage for SetNexWindow** functions @@ -633,7 +634,8 @@ struct ImGuiContext ImGuiNavForward NavMoveRequestForward; // None / ForwardQueued / ForwardActive (this is used to navigate sibling parent menus from a child menu) ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down) ImGuiDir NavMoveDirLast; // Direction of the previous move request - ImGuiNavMoveResult NavMoveResult; // Best move request candidate + ImGuiNavMoveResult NavMoveResultLocal; // Best move request candidate within NavWindow + ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using the NavFlattened flag) // Render ImDrawData DrawData; // Main ImDrawData instance to pass render information to the user From bed6ef03f5821437d6c697fc0607605aa3255ae5 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 29 Jan 2018 23:49:51 +0100 Subject: [PATCH 266/319] Nav: workaround to kindly handle ALT-TAB without detecting TAB-release on backends that clear all keys on unfocus. (#787) --- TODO.txt | 2 ++ imgui.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/TODO.txt b/TODO.txt index 0222bd59..0a46c81c 100644 --- a/TODO.txt +++ b/TODO.txt @@ -238,6 +238,8 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622) - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame) - focus: unable to use SetKeyboardFocusHere() on clipped widgets. (#787) + + - inputs: we need an explicit flag about whether the imgui window is focused, to be able to distinguish focused key releases vs alt-tabbing all release behaviors. - inputs: rework IO system to be able to pass actual ordered/timestamped events. use an event queue? (~#335, #71) - inputs: support track pad style scrolling & slider edit. diff --git a/imgui.cpp b/imgui.cpp index 965305ba..5f0ec833 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2780,9 +2780,11 @@ static void ImGui::NavUpdateWindowing() apply_focus_window = g.NavWindowingTarget; } - // Keyboard: Press and release ALT to toggle menu layer + // Keyboard: Press and Release ALT to toggle menu layer + // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of backend clearing releases all keys on ALT-TAB if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu, ImGuiInputReadMode_Released)) - apply_toggle_layer = true; + if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) + apply_toggle_layer = true; // Move window if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) From acf21ee429dc9d37ac03333e5113c6ea6fc119ac Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 30 Jan 2018 15:04:48 +0100 Subject: [PATCH 267/319] Nav: Windows with ImGuiWindowFlags_NoBringToFrontOnFocus flag aren't temporarily displayed on the front when using CTRL-TAB. (#787) --- imgui.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5f0ec833..8497bde6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3917,14 +3917,15 @@ void ImGui::Render() // Gather windows to render g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0; g.DrawDataBuilder.Clear(); + ImGuiWindow* window_to_render_front_most = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget : NULL; for (int n = 0; n != g.Windows.Size; n++) { ImGuiWindow* window = g.Windows[n]; - if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0 && window != g.NavWindowingTarget) + if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0 && window != window_to_render_front_most) AddWindowToDrawDataSelectLayer(window); } - if (g.NavWindowingTarget && g.NavWindowingTarget->Active && g.NavWindowingTarget->HiddenFrames <= 0) // NavWindowingTarget is always temporarily displayed as the front-most window - AddWindowToDrawDataSelectLayer(g.NavWindowingTarget); + if (window_to_render_front_most && window_to_render_front_most->Active && window_to_render_front_most->HiddenFrames <= 0) // NavWindowingTarget is always temporarily displayed as the front-most window + AddWindowToDrawDataSelectLayer(window_to_render_front_most); g.DrawDataBuilder.FlattenIntoSingleLayer(); // Draw software mouse cursor if requested From a7ad5134e5fcd8cfd95c29cd6087af767af7c4dd Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 30 Jan 2018 15:01:52 +0100 Subject: [PATCH 268/319] Nav: window that are filling the entire display/viewport gets their windowing highlight clamped within. (#787) --- imgui.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8497bde6..af833ceb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5760,13 +5760,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow()) window->DrawList->AddRectFilled(fullscreen_rect.Min, fullscreen_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); - // Draw navigation windowing rectangle (via ImGuiKey_NavWindowing key), shows whole window selected + // Draw navigation selection/windowing rectangle background if (g.NavWindowingTarget == window) { ImRect bb = window->Rect(); bb.Expand(g.FontSize); - window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), g.Style.WindowRounding, ~0, 3.0f); + if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway + window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); } // Draw window + handle manual resize @@ -5839,6 +5839,16 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize,-1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); } + // Draw navigation selection/windowing rectangle border + if (g.NavWindowingTarget == window) + { + ImRect bb = window->Rect(); + bb.Expand(g.FontSize); + if (bb.Contains(viewport_rect)) + bb.Expand(-g.FontSize - 2.0f); + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), g.Style.WindowRounding, ~0, 3.0f); + } + // Store a backup of SizeFull which we will use next frame to decide if we need scrollbars. window->SizeFullAtLastBegin = window->SizeFull; From a8763d14c5b06b7560170c696b5e47e5c9db971f Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 30 Jan 2018 15:07:20 +0100 Subject: [PATCH 269/319] Internals: Renaming. --- imgui.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index af833ceb..b566e1f4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -698,7 +698,7 @@ static void SaveIniSettingsToDisk(const char* ini_filename); static void SaveIniSettingsToMemory(ImVector& out_buf); static void MarkIniSettingsDirty(ImGuiWindow* window); -static ImRect GetVisibleRect(); +static ImRect GetViewportRect(); static void CloseInactivePopups(ImGuiWindow* ref_window); static void ClosePopupToLevel(int remaining); @@ -2629,7 +2629,7 @@ static ImVec2 NavCalcPreferredMousePos() return g.IO.MousePos; const ImRect& rect_rel = window->NavRectRel[g.NavLayer]; ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); - ImRect visible_rect = GetVisibleRect(); + ImRect visible_rect = GetViewportRect(); return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. } @@ -4592,7 +4592,7 @@ ImVec2 ImGui::GetItemRectSize() return window->DC.LastItemRect.GetSize(); } -static ImRect GetVisibleRect() +static ImRect GetViewportRect() { ImGuiContext& g = *GImGui; if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y) @@ -5046,7 +5046,7 @@ static ImVec2 FindBestWindowPosForPopup(const ImVec2& ref_pos, const ImVec2& siz // r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.) // r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it. ImVec2 safe_padding = style.DisplaySafeAreaPadding; - ImRect r_outer(GetVisibleRect()); + ImRect r_outer(GetViewportRect()); r_outer.Expand(ImVec2((size.x - r_outer.GetWidth() > safe_padding.x*2) ? -safe_padding.x : 0.0f, (size.y - r_outer.GetHeight() > safe_padding.y*2) ? -safe_padding.y : 0.0f)); ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255)); @@ -5750,15 +5750,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->Clear(); window->DrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); - ImRect fullscreen_rect(GetVisibleRect()); + ImRect viewport_rect(GetViewportRect()); if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true); else - PushClipRect(fullscreen_rect.Min, fullscreen_rect.Max, true); + PushClipRect(viewport_rect.Min, viewport_rect.Max, true); // Draw modal window background (darkens what is behind them) if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow()) - window->DrawList->AddRectFilled(fullscreen_rect.Min, fullscreen_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); + window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); // Draw navigation selection/windowing rectangle background if (g.NavWindowingTarget == window) From 1ed7bce3edf213c00632995929b0fc01bd5c4227 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 30 Jan 2018 18:29:53 +0100 Subject: [PATCH 270/319] Nav: Removed old unused code. (#787) --- imgui.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b566e1f4..1e0b6f63 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3839,15 +3839,6 @@ void ImGui::EndFrame() g.ActiveIdClickOffset = g.IO.MousePos - g.HoveredRootWindow->Pos; if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove) && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoMove)) g.MovingWindow = g.HoveredWindow; - - // FIXME-NAV: This never execute because of the FocusWindow call above, however we may want this behavior? - /* - if (g.NavWindow != g.HoveredWindow) - { - g.NavRefRectRel = ImRect(g.IO.MousePos - g.HoveredWindow->Pos, g.IO.MousePos - g.HoveredWindow->Pos); //ImRect(0,0,0,0); - g.NavDisableHighlight = true; - } - */ } else if (g.NavWindow != NULL && GetFrontMostModalRootWindow() == NULL) { From 9fc6f5907b7cfe70df4d1ebd81eff15f27c3d5a1 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 30 Jan 2018 18:34:32 +0100 Subject: [PATCH 271/319] Nav: Fixed NavRectRel being cleared for one frame when restoring layer 0 with Alt/Menu key or Escape. Maybe was inconsequential. (#787) --- imgui.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1e0b6f63..92cefa96 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2248,6 +2248,16 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) return new_best; } +static void NavRestoreLayer(int layer) +{ + ImGuiContext& g = *GImGui; + g.NavLayer = layer; + if (layer == 0 && g.NavWindow->NavLastIds[0] != 0) + SetNavIDAndMoveMouse(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]); + else + ImGui::NavInitWindow(g.NavWindow, true); +} + static inline void NavUpdateAnyRequestFlag() { ImGuiContext& g = *GImGui; @@ -2825,13 +2835,9 @@ static void ImGui::NavUpdateWindowing() { if ((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) == 0 && (g.NavWindow->RootWindow->DC.NavLayerActiveMask & (1 << 1)) != 0) FocusWindow(g.NavWindow->RootWindow); - g.NavLayer = (g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0; g.NavDisableHighlight = false; g.NavDisableMouseHover = true; - if (g.NavLayer == 0 && g.NavWindow->NavLastIds[0] != 0) - SetNavIDAndMoveMouse(g.NavWindow->NavLastIds[0], g.NavLayer, ImRect()); - else - NavInitWindow(g.NavWindow, true); + NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0); } } @@ -2971,11 +2977,7 @@ static void ImGui::NavUpdate() else if (g.NavLayer != 0) { // Leave the "menu" layer - g.NavLayer = 0; - if (g.NavWindow->NavLastIds[0]) - SetNavIDAndMoveMouse(g.NavWindow->NavLastIds[0], g.NavLayer, ImRect()); - else - NavInitWindow(g.NavWindow, true); + NavRestoreLayer(0); } else { From 22f7de0fbdeed04f6279f505d79ed03efa536f2b Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 30 Jan 2018 19:20:20 +0100 Subject: [PATCH 272/319] Nav: Updated TODO list. --- TODO.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/TODO.txt b/TODO.txt index 0a46c81c..f0ef26b9 100644 --- a/TODO.txt +++ b/TODO.txt @@ -229,10 +229,13 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - font: fix AddRemapChar() to work before font has been built. - font: (api breaking) removed "TTF" from symbol names. also because it now supports OTF. -!- nav/keyboard: tooltip & combo boxes are messing up / not honoring keyboard tabbing. - nav: integrate navigation branch into master. (#787) + - nav: Menu/Esc on a menu restore layer 0 but lose child window position. + - nav: Esc on a flattened child + - nav: menus: allow pressing Menu to leave a sub-menu. - nav: integrate/design keyboard controls. - - nav: once tab should go through most/all widgets (in submission order?) + - nav: simulate right-click or context activation? (SHIFT+F10) + - nav: tabs should go through most/all widgets (in submission order?) - nav: currently cannot access menubar of a child window with Alt/menu key. - nav: esc/enter default behavior for popups, e.g. be able to mark an "ok" or "cancel" button that would get triggered by those keys. - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622) From bd579e5429d4907549eb1bd8600d435c2b5cae06 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 30 Jan 2018 20:07:12 +0100 Subject: [PATCH 273/319] Nav: Removed io.NavUsable --> io.NavActive, io.NavActive --> io.NavVisible. (#787). NavActive takes account of NavFlags enable flags. --- imgui.cpp | 12 ++++++------ imgui.h | 4 ++-- imgui_demo.cpp | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 92cefa96..3b9753dd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -221,8 +221,8 @@ - For gamepad use, the easiest approach is to go all-or-nothing, with a buttons combo that toggle your inputs between imgui and your game/application. Sharing inputs in a more advanced or granular way between imgui and your game/application may be tricky and requires further work on imgui. For more advanced uses, you may want to use: - - io.NavUsable: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. - - io.NavActive: true when the navigation cursor is visible (and usually goes false when mouse is used). + - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. + - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). - query focus information with IsWindowFocused(), IsAnyWindowFocused(), IsAnyItemFocused() functions. The reality is more complex than what those flags can express. Please discuss your issues and usage scenario in the thread above! As we head toward more keyboard-oriented development this aspect will need to be improved. @@ -2281,7 +2281,7 @@ static void NavMoveRequestCancel() static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) { ImGuiContext& g = *GImGui; - //if (!g.IO.NavUsable) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag. + //if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag. // return; const ImGuiItemFlags item_flags = window->DC.ItemFlags; @@ -2947,8 +2947,8 @@ static void ImGui::NavUpdate() NavUpdateWindowing(); // Set output flags for user application - g.IO.NavUsable = g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); - g.IO.NavActive = (g.IO.NavUsable && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; + g.IO.NavActive = (g.IO.NavFlags & (ImGuiNavFlags_EnableGamepad | ImGuiNavFlags_EnableKeyboard)) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); + g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; // Process NavCancel input (to close a popup, get back to parent, clear focus) if (IsNavInputPressed(ImGuiNavInput_PadCancel, ImGuiInputReadMode_Pressed)) @@ -13017,7 +13017,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), ActiveIdSource: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, input_source_names[g.ActiveIdSource]); ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); ImGui::Text("NavWindow: '%s', NavId: 0x%08X, NavLayer: %d", g.NavWindow ? g.NavWindow->Name : "NULL", g.NavId, g.NavLayer); - ImGui::Text("NavUsable: %d, NavActive: %d", g.IO.NavUsable, g.IO.NavActive); + ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); diff --git a/imgui.h b/imgui.h index 8659f1d7..9a1fc5e1 100644 --- a/imgui.h +++ b/imgui.h @@ -1016,8 +1016,8 @@ struct ImGuiIO bool WantCaptureKeyboard; // When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. This is set by ImGui when it wants to use your keyboard inputs. bool WantTextInput; // Mobile/console: when io.WantTextInput is true, you may display an on-screen keyboard. This is set by ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). bool WantMoveMouse; // MousePos has been altered, back-end should reposition mouse on next frame. Set only when ImGuiNavFlags_MoveMouse flag is enabled in io.NavFlags. - bool NavUsable; // Directional navigation is currently allowed (will handle ImGuiKey_NavXXX events) ~ a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. - bool NavActive; // Directional navigation is active/visible and currently allowed (will handle ImGuiKey_NavXXX events). + bool NavActive; // Directional navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. + bool NavVisible; // Directional navigation is visible and allowed (will handle ImGuiKey_NavXXX events). float Framerate; // Application framerate estimation, in frame per second. Solely for convenience. Rolling average estimation based on IO.DeltaTime over 120 frames int MetricsAllocs; // Number of active memory allocations int MetricsRenderVertices; // Vertices output during last call to Render() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2746b8a0..06e8f711 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1818,7 +1818,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Text("Keys release:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyReleased(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); - ImGui::Text("NavUsable: %d, NavActive: %d", io.NavUsable, io.NavActive); + ImGui::Text("NavActive: %d, NavVisible: %d", io.NavActive, io.NavVisible); ImGui::Text("NavInputs down:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputs[i] > 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f", i, io.NavInputs[i]); } ImGui::Text("NavInputs pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] == 0.0f) { ImGui::SameLine(); ImGui::Text("[%d]", i); } ImGui::Text("NavInputs duration:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f", i, io.NavInputsDownDuration[i]); } From e6c0b212e8119fb171913553ebd9f44ff0c0b5c1 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 31 Jan 2018 18:53:15 +0100 Subject: [PATCH 274/319] Nav: Fixed initial movement (which sends an InitRequest) from clearing NavDisableHighlight and fully enabling Nav feedbacks. (#787) NB: Setting g.NavInitResultExplicit = false on InitRequest match was added in initial commit c2cb2a6928a436b1358f5e960844c02cbab0b3ba --- imgui.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index feef6b5f..58cfa3fb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2291,7 +2291,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) { - g.NavInitRequest = g.NavInitResultExplicit = false; // Found a match, clear request + g.NavInitRequest = false; // Found a match, clear request NavUpdateAnyRequestFlag(); } if (g.NavInitResultId == 0 || !(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) @@ -2887,10 +2887,11 @@ static void ImGui::NavUpdate() { // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) IM_ASSERT(g.NavWindow); - SetNavID(g.NavInitResultId, g.NavLayer); + if (g.NavInitResultExplicit) + SetNavIDAndMoveMouse(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel); + else + SetNavID(g.NavInitResultId, g.NavLayer); g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel; - if (g.NavDisableMouseHover) - g.NavMousePosDirty = true; } g.NavInitRequest = false; g.NavInitResultExplicit = false; From 38d45ee73f47a70ca1f9a81522a36d493ab1bf30 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 31 Jan 2018 19:13:20 +0100 Subject: [PATCH 275/319] Demo: Tweaked the Child demos, added a menu bar to the second child to test some navigation functions. --- imgui_demo.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9e92634f..852123fd 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1056,7 +1056,6 @@ void ImGui::ShowDemoWindow(bool* p_open) static bool disable_mouse_wheel = false; ImGui::Checkbox("Disable Mouse Wheel", &disable_mouse_wheel); - ImGui::Text("Without border"); static int line = 50; bool goto_line = ImGui::Button("Goto"); ImGui::SameLine(); @@ -1083,8 +1082,16 @@ void ImGui::ShowDemoWindow(bool* p_open) // Child 2: rounded border { ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f); - ImGui::BeginChild("Child2", ImVec2(0,300), true, (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0)); - ImGui::Text("With border"); + ImGui::BeginChild("Child2", ImVec2(0,300), true, (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0) | ImGuiWindowFlags_MenuBar); + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("Menu")) + { + ShowExampleMenuFile(); + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } ImGui::Columns(2); for (int i = 0; i < 100; i++) { From aa2dda7610a5c21daa905a26b8adec805b01a140 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 31 Jan 2018 21:06:47 +0100 Subject: [PATCH 276/319] Nav: Tweak code to be easier to follow + updated todo list after clarifying an issue. --- TODO.txt | 4 ++-- imgui.cpp | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/TODO.txt b/TODO.txt index 8f470110..05a8875e 100644 --- a/TODO.txt +++ b/TODO.txt @@ -235,8 +235,8 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav: menus: allow pressing Menu to leave a sub-menu. - nav: integrate/design keyboard controls. - nav: simulate right-click or context activation? (SHIFT+F10) - - nav: tabs should go through most/all widgets (in submission order?) - - nav: currently cannot access menubar of a child window with Alt/menu key. + - nav: tabs should go through most/all widgets (in submission order?). + - nav: cannot access menubar of a flattened child window with Alt/menu key (not a very common use case..). - nav: esc/enter default behavior for popups, e.g. be able to mark an "ok" or "cancel" button that would get triggered by those keys. - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622) - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame) diff --git a/imgui.cpp b/imgui.cpp index 58cfa3fb..372029ba 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2289,16 +2289,16 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) { // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback + if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0) + { + g.NavInitResultId = id; + g.NavInitResultRectRel = nav_bb_rel; + } if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) { g.NavInitRequest = false; // Found a match, clear request NavUpdateAnyRequestFlag(); } - if (g.NavInitResultId == 0 || !(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) - { - g.NavInitResultId = id; - g.NavInitResultRectRel = nav_bb_rel; - } } // Scoring for navigation @@ -13017,7 +13017,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec)", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), ActiveIdSource: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, input_source_names[g.ActiveIdSource]); ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); - ImGui::Text("NavWindow: '%s', NavId: 0x%08X, NavLayer: %d", g.NavWindow ? g.NavWindow->Name : "NULL", g.NavId, g.NavLayer); + ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL"); + ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); From 76d8af40369a8f32be1de52c5c1fc1cbf8591354 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 31 Jan 2018 21:25:52 +0100 Subject: [PATCH 277/319] Nav: when browsing a window that has no activable items (scroll only) we keep a highlight on the child. (#787) --- imgui.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 372029ba..937619ca 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4960,7 +4960,8 @@ bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWin void ImGui::EndChild() { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss if (window->BeginCount > 1) @@ -4977,13 +4978,18 @@ void ImGui::EndChild() sz.y = ImMax(4.0f, sz.y); End(); - ImGuiWindow* parent_window = GetCurrentWindow(); + ImGuiWindow* parent_window = g.CurrentWindow; ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); + ItemSize(sz); - if (!(window->Flags & ImGuiWindowFlags_NavFlattened) && (window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll)) + if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) { ItemAdd(bb, window->ChildId); RenderNavHighlight(bb, window->ChildId); + + // When browsing a window that has no activable items (scroll only) we keep a highlight on the child + if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow) + RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin); } else { From 7b22a91578800e115b5d3feacaa89707d80ab788 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 00:08:13 +0100 Subject: [PATCH 278/319] Nav / Slider: Fixed reaching edge of integer slider with navigation input, bug introduced on January 25 in Nav branch 10a4a77b27d598bf10fb617e4180488eaf0adc2a. (#787) --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index ac12e3ff..ebbd7522 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8457,10 +8457,11 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v } if (IsNavInputDown(ImGuiNavInput_PadTweakFast)) delta *= 10.0f; - clicked_t = ImSaturate(clicked_t + delta); set_new_value = true; if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits set_new_value = false; + else + clicked_t = ImSaturate(clicked_t + delta); } } else From bdd868704f8cf92384847f98a5e354f511a296e1 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 00:50:42 +0100 Subject: [PATCH 279/319] Nav: Child window is restored on focus when returning to layer 0 or refocusing. This is a little experimental and potentially error-prone right now. (#787, vaguely relate to ~#727) Ideally we should maintain a non-sorted last-focused list that include childs windows. --- TODO.txt | 1 - imgui.cpp | 46 ++++++++++++++++++++++++++++++++++++++-------- imgui_internal.h | 1 + 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/TODO.txt b/TODO.txt index 05a8875e..a483594f 100644 --- a/TODO.txt +++ b/TODO.txt @@ -230,7 +230,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - font: (api breaking) removed "TTF" from symbol names. also because it now supports OTF. - nav: integrate navigation branch into master. (#787) - - nav: Menu/Esc on a menu restore layer 0 but lose child window position. - nav: Esc on a flattened child - nav: menus: allow pressing Menu to leave a sub-menu. - nav: integrate/design keyboard controls. diff --git a/imgui.cpp b/imgui.cpp index ebbd7522..fc4e0420 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1919,6 +1919,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) NavRootWindow = NULL; NavLastIds[0] = NavLastIds[1] = 0; NavRectRel[0] = NavRectRel[1] = ImRect(); + NavLastChildNavWindow = NULL; FocusIdxAllCounter = FocusIdxTabCounter = -1; FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX; @@ -1983,7 +1984,7 @@ static void SetNavID(ImGuiID id, int nav_layer) IM_ASSERT(g.NavWindow); IM_ASSERT(nav_layer == 0 || nav_layer == 1); g.NavId = id; - g.NavWindow->NavLastIds[nav_layer] = g.NavId; + g.NavWindow->NavLastIds[nav_layer] = id; } static void SetNavIDAndMoveMouse(ImGuiID id, int nav_layer, const ImRect& rect_rel) @@ -2248,10 +2249,22 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) return new_best; } +// Call when we are expected to land on Layer 0 after FocusWindow() +static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window) +{ + ImGuiWindow* child_window = window->NavLastChildNavWindow; + if (child_window == NULL) + return window; + window->NavLastChildNavWindow = NULL; + return child_window; +} + static void NavRestoreLayer(int layer) { ImGuiContext& g = *GImGui; g.NavLayer = layer; + if (layer == 0) + g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); if (layer == 0 && g.NavWindow->NavLastIds[0] != 0) SetNavIDAndMoveMouse(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]); else @@ -2819,6 +2832,7 @@ static void ImGui::NavUpdateWindowing() { g.NavDisableHighlight = false; g.NavDisableMouseHover = true; + apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); FocusWindow(apply_focus_window); if (apply_focus_window->NavLastIds[0] == 0) NavInitWindow(apply_focus_window, false); @@ -2833,8 +2847,14 @@ static void ImGui::NavUpdateWindowing() // Apply menu/layer toggle if (apply_toggle_layer && g.NavWindow) { + // FIXME-NAV: Iterate parent windows to find first one with an active menu layer, instead of aiming at root window immediately if ((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) == 0 && (g.NavWindow->RootWindow->DC.NavLayerActiveMask & (1 << 1)) != 0) - FocusWindow(g.NavWindow->RootWindow); + { + ImGuiWindow* old_nav_window = g.NavWindow; + ImGuiWindow* new_nav_window = g.NavWindow->RootWindow; + FocusWindow(new_nav_window); + new_nav_window->NavLastChildNavWindow = old_nav_window; + } g.NavDisableHighlight = false; g.NavDisableMouseHover = true; NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0); @@ -2945,6 +2965,12 @@ static void ImGui::NavUpdate() g.NavJustTabbedId = 0; IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); + // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 + if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow) + g.NavWindow->RootWindow->NavLastChildNavWindow = g.NavWindow; + if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0) + g.NavWindow->NavLastChildNavWindow = NULL; + NavUpdateWindowing(); // Set output flags for user application @@ -4731,10 +4757,10 @@ static ImGuiWindow* GetFrontMostModalRootWindow() static void ClosePopupToLevel(int remaining) { ImGuiContext& g = *GImGui; - if (remaining > 0) - ImGui::FocusWindow(g.OpenPopupStack[remaining-1].Window); - else - ImGui::FocusWindow(g.OpenPopupStack[0].ParentWindow); + ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow; + if (g.NavLayer == 0) + focus_window = NavRestoreLastChildNavWindow(focus_window); + ImGui::FocusWindow(focus_window); g.OpenPopupStack.resize(remaining); } @@ -6210,9 +6236,11 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId g.NavIdIsAlive = false; g.NavLayer = 0; + g.NavWindow = window; if (window && g.NavDisableMouseHover) g.NavMousePosDirty = true; - g.NavWindow = window; + if (window && window->NavLastChildNavWindow != NULL) + window->NavLastChildNavWindow = NULL; } // Passing NULL allow to disable keyboard focus @@ -6239,7 +6267,8 @@ void ImGui::FocusFrontMostActiveWindow(ImGuiWindow* ignore_window) for (int i = g.Windows.Size - 1; i >= 0; i--) if (g.Windows[i] != ignore_window && g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow)) { - FocusWindow(g.Windows[i]); + ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(g.Windows[i]); + FocusWindow(focus_window); return; } } @@ -12994,6 +13023,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetScrollMaxX(window), window->Scroll.y, GetScrollMaxY(window)); ImGui::BulletText("Active: %d, WriteAccessed: %d", window->Active, window->WriteAccessed); ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); + ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); diff --git a/imgui_internal.h b/imgui_internal.h index 7a691cb7..23e6cfc0 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -940,6 +940,7 @@ struct IMGUI_API ImGuiWindow ImGuiWindow* RootNonPopupWindow; // Generally point to ourself. Used to display TitleBgActive color and for selecting which window to use for NavWindowing ImGuiWindow* NavRootWindow; // Generally point to ourself. If we are a child window with the NavFlattened flag, point to a parent window. + ImGuiWindow* NavLastChildNavWindow; // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.) ImGuiID NavLastIds[2]; // Last known NavId for this window, per layer (0/1) ImRect NavRectRel[2]; // Reference rectangle, in window relative space From 8cc2dbc3bded49f7b3707b949e61966fce50af66 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 14:56:56 +0100 Subject: [PATCH 280/319] Internals: Nav: Extracted code into IsWindowNavFocusable(). (#787) --- imgui.cpp | 14 +++++++++----- imgui_internal.h | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index fc4e0420..6ef58a62 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2669,11 +2669,8 @@ static ImGuiWindow* FindWindowNavigable(int i_start, int i_stop, int dir) // FIX { ImGuiContext& g = *GImGui; for (int i = i_start; i >= 0 && i < g.Windows.Size && i != i_stop; i += dir) - { - ImGuiWindow* window = g.Windows[i]; - if (window->Active && window == window->RootNonPopupWindow && (!(window->Flags & ImGuiWindowFlags_NoNavFocus) || window == g.NavWindow)) - return window; - } + if (ImGui::IsWindowNavFocusable(g.Windows[i])) + return g.Windows[i]; return NULL; } @@ -6650,6 +6647,13 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) } } +// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext) +bool ImGui::IsWindowNavFocusable(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + return window->Active && window == window->RootNonPopupWindow && (!(window->Flags & ImGuiWindowFlags_NoNavFocus) || window == g.NavWindow); +} + float ImGui::GetWindowWidth() { ImGuiWindow* window = GImGui->CurrentWindow; diff --git a/imgui_internal.h b/imgui_internal.h index 23e6cfc0..a44eba4f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1001,6 +1001,7 @@ namespace ImGui IMGUI_API void BringWindowToFront(ImGuiWindow* window); IMGUI_API void BringWindowToBack(ImGuiWindow* window); IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); + IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); IMGUI_API void Initialize(); From ef2c3bcdee9af118a1345488dad53dd7bf04bec1 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 18:02:55 +0100 Subject: [PATCH 281/319] Nav: Fixed InitRequest leaking when changing window (repro was to navigate inside File->Options->[Child] then press Left and notice how we would land on parent window on the fist item after the current one). (#787) --- imgui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 6ef58a62..daf39ec7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6238,6 +6238,8 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavMousePosDirty = true; if (window && window->NavLastChildNavWindow != NULL) window->NavLastChildNavWindow = NULL; + if (g.NavInitRequest) + g.NavInitRequest = false; } // Passing NULL allow to disable keyboard focus From fd6d8863f77c87d642d1a6108f061a0315d2daf0 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 18:19:36 +0100 Subject: [PATCH 282/319] Nav: SetFocusID() clears NavInitRequest for consistency (repro would a same frame interaction / race condition). (#787) --- imgui.cpp | 5 +++-- imgui_internal.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index daf39ec7..e9795c8d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2027,6 +2027,8 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it. const int nav_layer = window->DC.NavLayerCurrent; + if (g.NavWindow != window) + g.NavInitRequest = false; g.NavId = id; g.NavWindow = window; g.NavLayer = nav_layer; @@ -6238,8 +6240,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavMousePosDirty = true; if (window && window->NavLastChildNavWindow != NULL) window->NavLastChildNavWindow = NULL; - if (g.NavInitRequest) - g.NavInitRequest = false; + g.NavInitRequest = false; } // Passing NULL allow to disable keyboard focus diff --git a/imgui_internal.h b/imgui_internal.h index a44eba4f..0c93de71 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -621,7 +621,7 @@ struct ImGuiContext int NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid - bool NavMousePosDirty; + bool NavMousePosDirty; // When set we will update mouse position if (NavFlags & ImGuiNavFlags_MoveMouse) if set bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (nb: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest From 5812d0b75108a21f2436e668d140bda59154a8ac Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 18:29:30 +0100 Subject: [PATCH 283/319] Nav: Using CTRL+TAB / PadFocusNext/Prev to Focus a window closes the previous window popups. (#787) --- imgui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.cpp b/imgui.cpp index e9795c8d..e3b41300 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2832,6 +2832,7 @@ static void ImGui::NavUpdateWindowing() g.NavDisableHighlight = false; g.NavDisableMouseHover = true; apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); + CloseInactivePopups(apply_focus_window); FocusWindow(apply_focus_window); if (apply_focus_window->NavLastIds[0] == 0) NavInitWindow(apply_focus_window, false); From 95f9c74b9ae857529675489b4ce99490671dcf57 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 19:21:44 +0100 Subject: [PATCH 284/319] Metrics: Displaying some of the important internal window flags. --- imgui.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 34cc1851..a3b72926 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13025,13 +13025,20 @@ void ImGui::ShowMetricsWindow(bool* p_open) { if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window)) return; + ImGuiWindowFlags flags = window->Flags; NodeDrawList(window, window->DrawList, "DrawList"); ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); + ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s..)", flags, + (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", + (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : ""); ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetScrollMaxX(window), window->Scroll.y, GetScrollMaxY(window)); ImGui::BulletText("Active: %d, WriteAccessed: %d", window->Active, window->WriteAccessed); ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); - ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); + if (window->NavRectRel[0].IsFinite()) + ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); + else + ImGui::BulletText("NavRectRel[0]: "); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); From eb737e0a427bac0a79969a91a4bddd0486efaec0 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 21:26:52 +0100 Subject: [PATCH 285/319] Nav: Child window is restored on focus follow up. Fix bdd868704f8cf92384847f98a5e354f511a296e1. (#787, ~#727) One visible issue was pressing Left to leave a child menu. --- imgui.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a3b72926..8febc940 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2250,14 +2250,19 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) return new_best; } +static void NavSaveLastChildNavWindow(ImGuiWindow* child_window) +{ + ImGuiWindow* parent_window = child_window; + while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) + parent_window = parent_window->ParentWindow; + if (parent_window && parent_window != child_window) + parent_window->NavLastChildNavWindow = child_window; +} + // Call when we are expected to land on Layer 0 after FocusWindow() static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window) { - ImGuiWindow* child_window = window->NavLastChildNavWindow; - if (child_window == NULL) - return window; - window->NavLastChildNavWindow = NULL; - return child_window; + return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window; } static void NavRestoreLayer(int layer) @@ -2846,11 +2851,12 @@ static void ImGui::NavUpdateWindowing() // Apply menu/layer toggle if (apply_toggle_layer && g.NavWindow) { - // FIXME-NAV: Iterate parent windows to find first one with an active menu layer, instead of aiming at root window immediately - if ((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) == 0 && (g.NavWindow->RootWindow->DC.NavLayerActiveMask & (1 << 1)) != 0) + ImGuiWindow* new_nav_window = g.NavWindow; + while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) + new_nav_window = new_nav_window->ParentWindow; + if (new_nav_window != g.NavWindow) { ImGuiWindow* old_nav_window = g.NavWindow; - ImGuiWindow* new_nav_window = g.NavWindow->RootWindow; FocusWindow(new_nav_window); new_nav_window->NavLastChildNavWindow = old_nav_window; } @@ -2965,8 +2971,8 @@ static void ImGui::NavUpdate() IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 - if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow) - g.NavWindow->RootWindow->NavLastChildNavWindow = g.NavWindow; + if (g.NavWindow) + NavSaveLastChildNavWindow(g.NavWindow); if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0) g.NavWindow->NavLastChildNavWindow = NULL; @@ -6238,8 +6244,6 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavWindow = window; if (window && g.NavDisableMouseHover) g.NavMousePosDirty = true; - if (window && window->NavLastChildNavWindow != NULL) - window->NavLastChildNavWindow = NULL; g.NavInitRequest = false; } From 5c9ea4d53a864e412d01be3a0a9ac36062b0d5c7 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 21:33:23 +0100 Subject: [PATCH 286/319] Nav: Fixed press Left on sub-menu when parent wasn't a menu - we were just checking at the wrong level. (#787) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 8febc940..01b9d0f8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11099,7 +11099,7 @@ void ImGui::EndMenu() // Nav: When a left move request within our child menu failed, close the menu ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - if (g.NavWindow && g.NavWindow->ParentWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveDir == ImGuiDir_Left && window->DC.LayoutType == ImGuiLayoutType_Vertical) + if (g.NavWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) { ClosePopupToLevel(g.OpenPopupStack.Size - 1); NavMoveRequestCancel(); From c7b7b181b5ae1253bbbb3a6bf0ff6241ebaad5bb Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 22:13:25 +0100 Subject: [PATCH 287/319] Nav: CloseButton reacts when clipped. (#787) --- imgui.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 01b9d0f8..4bec647c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7564,17 +7564,21 @@ bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg) return pressed; } -// Upper-right button to close a window. +// Button to close a window bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + // We intentionally allow interaction when clipped so that a mechanical Alt,Right,Validate sequence close a window. + // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible). const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius)); - if (!ItemAdd(bb, id)) // To allow navigation - return false; + bool is_clipped = !ItemAdd(bb, id); bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held); + if (is_clipped) + return pressed; // Render const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton); @@ -7587,7 +7591,6 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), GetColorU32(ImGuiCol_Text)); window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), GetColorU32(ImGuiCol_Text)); } - return pressed; } From 950f260a32538967e4a063c3259220013aee136e Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 22:25:25 +0100 Subject: [PATCH 288/319] Nav: Fixed Selectable/MenuItem Nav Highlight from using rounding when outer highlight stays square. (#787) --- imgui.cpp | 7 ++++--- imgui_internal.h | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4bec647c..fce0c566 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4248,6 +4248,7 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl return; ImGuiWindow* window = ImGui::GetCurrentWindow(); + float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; ImRect display_rect = bb; display_rect.ClipWith(window->ClipRect); if (flags & ImGuiNavHighlightFlags_TypeDefault) @@ -4258,13 +4259,13 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl bool fully_visible = window->ClipRect.Contains(display_rect); if (!fully_visible) window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); - window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), g.Style.FrameRounding, 0x0F, THICKNESS); + window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS); if (!fully_visible) window->DrawList->PopClipRect(); } if (flags & ImGuiNavHighlightFlags_TypeThin) { - window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), g.Style.FrameRounding, ~0, 1.0f); + window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f); } } @@ -10690,7 +10691,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl { const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f); - RenderNavHighlight(bb_with_spacing, id, ImGuiNavHighlightFlags_TypeThin); + RenderNavHighlight(bb_with_spacing, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); } if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) diff --git a/imgui_internal.h b/imgui_internal.h index 4531f732..97fefb2f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -282,7 +282,8 @@ enum ImGuiNavHighlightFlags_ { ImGuiNavHighlightFlags_TypeDefault = 1 << 0, ImGuiNavHighlightFlags_TypeThin = 1 << 1, - ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2 + ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, + ImGuiNavHighlightFlags_NoRounding = 1 << 3 }; enum ImGuiNavDirSourceFlags_ From 68d3e139a74ed9d7cad4abb0f36466544ef24620 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 22:28:58 +0100 Subject: [PATCH 289/319] Nav: When focusing a parent window while closing a popup, hide its highlight for one frame to avoid potential double highlight and flicker with the common pattern of menu items leading to the opening other windows. (#787) --- imgui.cpp | 6 +++++- imgui_internal.h | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index fce0c566..60863f3d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4247,6 +4247,8 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw)) return; ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->DC.NavHideHighlightOneFrame) + return; float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; ImRect display_rect = bb; @@ -4767,6 +4769,7 @@ static void ClosePopupToLevel(int remaining) if (g.NavLayer == 0) focus_window = NavRestoreLastChildNavWindow(focus_window); ImGui::FocusWindow(focus_window); + focus_window->DC.NavHideHighlightOneFrame = true; g.OpenPopupStack.resize(remaining); } @@ -5901,9 +5904,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.CursorMaxPos = window->DC.CursorStartPos; window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f; window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; + window->DC.NavHideHighlightOneFrame = false; + window->DC.NavHasScroll = (GetScrollMaxY() > 0.0f); window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext; window->DC.NavLayerActiveMaskNext = 0x00; - window->DC.NavHasScroll = (GetScrollMaxY() > 0.0f); window->DC.MenuBarAppending = false; window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x); window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; diff --git a/imgui_internal.h b/imgui_internal.h index 97fefb2f..37b32651 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -820,6 +820,7 @@ struct IMGUI_API ImGuiDrawContext ImGuiID LastItemId; ImRect LastItemRect; bool LastItemRectHoveredRect; + bool NavHideHighlightOneFrame; bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) int NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) int NavLayerCurrentMask; // = (1 << NavLayerCurrent) used by ItemAdd prior to clipping. @@ -857,6 +858,7 @@ struct IMGUI_API ImGuiDrawContext LastItemId = 0; LastItemRect = ImRect(); LastItemRectHoveredRect = false; + NavHideHighlightOneFrame = false; NavHasScroll = false; NavLayerActiveMask = NavLayerActiveMaskNext = 0x00; NavLayerCurrent = 0; From 942c1407109577acfd19d9f8539d03a7882b8c71 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 22:32:52 +0100 Subject: [PATCH 290/319] CloseButton: Fixed cross positioning. --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 60863f3d..bb0b62d6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7587,12 +7587,13 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) // Render const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton); - const ImVec2 center = bb.GetCenter(); + ImVec2 center = bb.GetCenter(); window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), col, 12); const float cross_extent = (radius * 0.7071f) - 1.0f; if (hovered) { + center -= ImVec2(0.5f, 0.5f); window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), GetColorU32(ImGuiCol_Text)); window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), GetColorU32(ImGuiCol_Text)); } From efbd8cebe7bd7a410bc8e15fb45de22936bb4a39 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 22:43:37 +0100 Subject: [PATCH 291/319] Nav: Comments (#787) --- TODO.txt | 3 +++ imgui.cpp | 15 ++++++++------- imgui_demo.cpp | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/TODO.txt b/TODO.txt index a483594f..6f6ab7cc 100644 --- a/TODO.txt +++ b/TODO.txt @@ -157,6 +157,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - tooltip: allow tooltips with timers? or general timer policy? (instaneous vs timed) - menus: calling BeginMenu() twice with a same name doesn't append as Begin() does for regular windows (#1207) + - menus: menu bars inside modals windows are acting weird. - statusbar: add a per-window status bar helper similar to what menubar does. - shortcuts: local-style shortcut api, e.g. parse "&Save" - shortcuts,menus: global-style shortcut api e.g. "Save (CTRL+S)" -> explicit flag for recursing into closed menu @@ -230,6 +231,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - font: (api breaking) removed "TTF" from symbol names. also because it now supports OTF. - nav: integrate navigation branch into master. (#787) + - nav: Left within a tree node block as a fallback. - nav: Esc on a flattened child - nav: menus: allow pressing Menu to leave a sub-menu. - nav: integrate/design keyboard controls. @@ -237,6 +239,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav: tabs should go through most/all widgets (in submission order?). - nav: cannot access menubar of a flattened child window with Alt/menu key (not a very common use case..). - nav: esc/enter default behavior for popups, e.g. be able to mark an "ok" or "cancel" button that would get triggered by those keys. + - nav: when activating a button that changes label (without a static ID) or disappear, can we somehow automatically recover into a nearest highlight item? - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622) - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame) - focus: unable to use SetKeyboardFocusHere() on clipped widgets. (#787) diff --git a/imgui.cpp b/imgui.cpp index bb0b62d6..bd0ec45a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -211,26 +211,27 @@ USING GAMEPAD/KEYBOARD NAVIGATION [BETA] - - Gamepad/keyboard navigation support is available, currently in Beta with some issues. Your feedback and bug reports are welcome. + - Gamepad/keyboard navigation support is now available. Your feedback and bug reports are greatly welcome! - See https://github.com/ocornut/imgui/issues/787 discussion thread and ask questions there. - - The current primary focus is to support game controllers. - - Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. - - Consider using Synergy host (on your computer) + uSynergy.c (in your console/tablet/phone app) to use PC mouse/keyboard. + - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. - Your inputs are passed to imgui by filling the io.NavInputs[] array. See 'enum ImGuiNavInput_' in imgui.h for a description of available inputs. + - Please refer to the examples/ application for suggested keyboard and gamepad mapping. + - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. + - Consoles/Tablet/Phone users: Consider using Synergy host (on your computer) + uSynergy.c (in your console/tablet/phone app) to use your PC mouse/keyboard. - The ImGuiNavFlags_EnableGamepad and ImGuiNavFlags_EnableKeyboard flags of io.NavFlags are only here to instruct your binding whether to find inputs. - For gamepad use, the easiest approach is to go all-or-nothing, with a buttons combo that toggle your inputs between imgui and your game/application. Sharing inputs in a more advanced or granular way between imgui and your game/application may be tricky and requires further work on imgui. For more advanced uses, you may want to use: - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). - - query focus information with IsWindowFocused(), IsAnyWindowFocused(), IsAnyItemFocused() functions. + - query focus information with e.g. IsWindowFocused(), IsItemFocused() etc. functions. The reality is more complex than what those flags can express. Please discuss your issues and usage scenario in the thread above! As we head toward more keyboard-oriented development this aspect will need to be improved. - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiNavFlags_MoveMouse flag in io.NavFlags. - Enabling ImGuiNavFlags_MoveMouse instructs dear imgui to move your mouse cursor along when navigation movement. + Enabling ImGuiNavFlags_MoveMouse instructs dear imgui to move your mouse cursor along with navigation movement. When enabled, the NewFrame() functions may alter 'io.MousePos' and set 'io.WantMoveMouse' to notify you that it did so. When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that. - (Important: It you set the ImGuiNavFlags_MoveMouse flag but don't honor 'io.WantMoveMouse' properly, imgui will misbehave as it will think your mouse is moving back and forth.) + (If you set the ImGuiNavFlags_MoveMouse flag but don't honor 'io.WantMoveMouse' properly, imgui will misbehave as it will see your mouse as moving back and forth.) (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want to set a boolean to ignore your other external mouse positions until the external source is moved again.) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2de0f632..3af5399b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1533,7 +1533,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::OpenPopup("Delete?"); if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n"); + ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n"); ImGui::Separator(); //static int dummy_i = 0; From 96ddfbc97342c8c4750cf66a05cae285afc26e6f Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 23:36:22 +0100 Subject: [PATCH 292/319] Nav: Modal windows can't be closed with Nav PadCancel. (#787) --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index bd0ec45a..a1ab36eb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3005,7 +3005,8 @@ static void ImGui::NavUpdate() else if (g.OpenPopupStack.Size > 0) { // Close open popup/menu - ClosePopupToLevel(g.OpenPopupStack.Size - 1); + if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) + ClosePopupToLevel(g.OpenPopupStack.Size - 1); } else if (g.NavLayer != 0) { From 91209382002eb9bd36c0dc8c91f83e755bfc9844 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Feb 2018 23:53:00 +0100 Subject: [PATCH 293/319] Nav: Revert 5c9ea4d53a864e412d01be3a0a9ac36062b0d5c7 with comments. (#787) --- imgui.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a1ab36eb..a62fe782 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11107,10 +11107,12 @@ bool ImGui::BeginMenu(const char* label, bool enabled) void ImGui::EndMenu() { - // Nav: When a left move request within our child menu failed, close the menu + // Nav: When a left move request _within our child menu_ failed, close the menu. + // A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs. + // However it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction. ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - if (g.NavWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) + if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) { ClosePopupToLevel(g.OpenPopupStack.Size - 1); NavMoveRequestCancel(); From ce9d7baaba09df15ca8afc54baef1cff15ae72ee Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 2 Feb 2018 17:47:05 +0100 Subject: [PATCH 294/319] Nav: TreeNode: Added ImGuiTreeNodeFlags_NavCloseFromChild flag, allow closing a TreeNode() from any of child. The explicit flag is not great, perhaps allowing some form of inheritance would help. (#787, #1079) --- imgui.cpp | 22 +++++++++++++++++++++- imgui.h | 1 + imgui_internal.h | 2 ++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index a62fe782..210132a7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5924,6 +5924,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.TextWrapPosStack.resize(0); window->DC.ColumnsSet = NULL; window->DC.TreeDepth = 0; + window->DC.TreeDepthMayCloseOnPop = 0x00; window->DC.StateStorage = &window->StateStorage; window->DC.GroupStack.resize(0); window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user); @@ -7883,6 +7884,13 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not) const ImRect interact_bb = display_frame ? bb : ImRect(bb.Min.x, bb.Min.y, bb.Min.x + text_width + style.ItemSpacing.x*2, bb.Max.y); bool is_open = TreeNodeBehaviorIsOpen(id, flags); + + // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child. + // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). + // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero. + if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavCloseFromChild) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + window->DC.TreeDepthMayCloseOnPop |= (1 << window->DC.TreeDepth); + if (!ItemAdd(interact_bb, id)) { if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) @@ -12529,9 +12537,21 @@ void ImGui::TreePushRawID(ImGuiID id) void ImGui::TreePop() { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; Unindent(); + window->DC.TreeDepth--; + if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) + if (g.NavIdIsAlive && (window->DC.TreeDepthMayCloseOnPop & (1 << window->DC.TreeDepth))) + { + ImGuiID id = window->IDStack.back(); + window->DC.StateStorage->SetBool(id, false); + SetNavID(id, g.NavLayer); + NavMoveRequestCancel(); + } + window->DC.TreeDepthMayCloseOnPop &= (1 << window->DC.TreeDepth) - 1; + PopID(); } diff --git a/imgui.h b/imgui.h index 5238b8c9..d441497c 100644 --- a/imgui.h +++ b/imgui.h @@ -602,6 +602,7 @@ enum ImGuiTreeNodeFlags_ ImGuiTreeNodeFlags_FramePadding = 1 << 10, // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding(). //ImGuITreeNodeFlags_SpanAllAvailWidth = 1 << 11, // FIXME: TODO: Extend hit box horizontally even if not framed //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 12, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible + ImGuiTreeNodeFlags_NavCloseFromChild = 1 << 13, // (WIP) Nav: left direction may close this TreeNode() when focusing on any child (items submitted between TreeNode and TreePop) ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoAutoOpenOnLog // Obsolete names (will be removed) diff --git a/imgui_internal.h b/imgui_internal.h index 37b32651..9b6bc742 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -817,6 +817,7 @@ struct IMGUI_API ImGuiDrawContext float PrevLineTextBaseOffset; float LogLinePosY; int TreeDepth; + ImU32 TreeDepthMayCloseOnPop; // Store a copy of !g.NavIdIsAlive for TreeDepth 0..31 ImGuiID LastItemId; ImRect LastItemRect; bool LastItemRectHoveredRect; @@ -855,6 +856,7 @@ struct IMGUI_API ImGuiDrawContext CurrentLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f; LogLinePosY = -1.0f; TreeDepth = 0; + TreeDepthMayCloseOnPop = 0x00; LastItemId = 0; LastItemRect = ImRect(); LastItemRectHoveredRect = false; From 648de2fc00e7960c89aac7a8826aea758d6894a5 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 2 Feb 2018 18:38:18 +0100 Subject: [PATCH 295/319] Nav: Press Left on a child with ImGuiTreeNodeFlags_NavCloseFromChild moves you to parent node instead of closing it immediately. More standard. (#787, #1079) --- imgui.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 210132a7..a0e7e2c4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12545,9 +12545,7 @@ void ImGui::TreePop() if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) if (g.NavIdIsAlive && (window->DC.TreeDepthMayCloseOnPop & (1 << window->DC.TreeDepth))) { - ImGuiID id = window->IDStack.back(); - window->DC.StateStorage->SetBool(id, false); - SetNavID(id, g.NavLayer); + SetNavID(window->IDStack.back(), g.NavLayer); NavMoveRequestCancel(); } window->DC.TreeDepthMayCloseOnPop &= (1 << window->DC.TreeDepth) - 1; From e2654a097bd88e0190a5ebf50fe23fba574516e0 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 4 Feb 2018 18:30:37 +0100 Subject: [PATCH 296/319] ImRect: added IsInverted() helper. --- imgui_internal.h | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui_internal.h b/imgui_internal.h index 9b6bc742..c012f380 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -332,6 +332,7 @@ struct IMGUI_API ImRect void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped. void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; } void FixInverted() { if (Min.x > Max.x) ImSwap(Min.x, Max.x); if (Min.y > Max.y) ImSwap(Min.y, Max.y); } + bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } bool IsFinite() const { return Min.x != FLT_MAX; } }; From 66ff820eaaebabe99aa89428af82c3fb3e32873a Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 4 Feb 2018 18:30:46 +0100 Subject: [PATCH 297/319] Nav: Ensure g.NavScoringRectScreen is always finite and not inverted. (#787) --- imgui.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 70d39e98..24cfe986 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2130,8 +2130,10 @@ static ImGuiDir NavScoreItemGetQuadrant(float dx, float dy) static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1) { - if (a1 < b0) return a1 - b0; - if (b1 < a0) return a0 - b1; + if (a1 < b0) + return a1 - b0; + if (b1 < a0) + return a0 - b1; return 0.0f; } @@ -3133,9 +3135,11 @@ static void ImGui::NavUpdate() } // 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.NavWindow->NavRectRel[g.NavLayer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[g.NavLayer].Max) : ImRect(); + ImRect nav_rect_rel = (g.NavWindow && g.NavWindow->NavRectRel[g.NavLayer].IsFinite()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0); + g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect(); g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; + IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous fabsf() calls in NavScoreItem(). //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] #if IMGUI_DEBUG_NAV_RECTS if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) g.OverlayDrawList.AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] From 223be68d7759c0eda5acae06de1663b3e64ea196 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 00:05:13 +0100 Subject: [PATCH 298/319] Nav: Debugging stuff. --- imgui.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 24cfe986..3686c5e4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2198,14 +2198,25 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) } #if IMGUI_DEBUG_NAV_SCORING + char buf[128]; if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) { - char buf[128]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f) dcen (%.2f,%.2f->%.4f) d (%.2f,%.2f->%.4f) nav %c, quad %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); + ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); + g.OverlayDrawList.AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100)); g.OverlayDrawList.AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); g.OverlayDrawList.AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150)); g.OverlayDrawList.AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); } + else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. + { + if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; } + if (quadrant == g.NavMoveDir) + { + ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); + g.OverlayDrawList.AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); + g.OverlayDrawList.AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); + } + } #endif // Is it in the quadrant we're interesting in moving to? From 2081fc15b51f5218f3e20845a1b9b49707b93a76 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 12:46:13 +0100 Subject: [PATCH 299/319] Added assert in BeginChild(ImGuiId id). --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 3686c5e4..e337c97b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3009,8 +3009,8 @@ static void ImGui::NavUpdate() // Exit child window ImGuiWindow* child_window = g.NavWindow; ImGuiWindow* parent_window = g.NavWindow->ParentWindow; - FocusWindow(parent_window); IM_ASSERT(child_window->ChildId != 0); + FocusWindow(parent_window); SetNavID(child_window->ChildId, 0); g.NavIdIsAlive = false; if (g.NavDisableMouseHover) @@ -5009,6 +5009,7 @@ bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) { + IM_ASSERT(id != 0); return BeginChildEx(NULL, id, size_arg, border, extra_flags); } From 8ad88f725c075782cab4849ac1d4389885e3ab22 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 14:53:07 +0100 Subject: [PATCH 300/319] Nav: Minor tidying up. (#787) --- imgui.cpp | 7 +++---- imgui.h | 1 + imgui_demo.cpp | 2 +- imgui_internal.h | 20 ++++++++++---------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e337c97b..0da2d40a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5035,7 +5035,6 @@ void ImGui::EndChild() ImGuiWindow* parent_window = g.CurrentWindow; ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); - ItemSize(sz); if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) { @@ -6265,13 +6264,13 @@ void ImGui::FocusWindow(ImGuiWindow* window) if (g.NavWindow != window) { - g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId - g.NavIdIsAlive = false; - g.NavLayer = 0; g.NavWindow = window; if (window && g.NavDisableMouseHover) g.NavMousePosDirty = true; g.NavInitRequest = false; + g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId + g.NavIdIsAlive = false; + g.NavLayer = 0; } // Passing NULL allow to disable keyboard focus diff --git a/imgui.h b/imgui.h index 9a80026c..61ff2c69 100644 --- a/imgui.h +++ b/imgui.h @@ -455,6 +455,7 @@ namespace ImGui IMGUI_API void StyleColorsLight(ImGuiStyle* dst = NULL); // Focus, Activation + // (Prefer using "SetItemDefaultFocus()" over "if (IsWindowAppearing()) SetScrollHere()" when applicable, to make your code more forward compatible when navigation branch is merged) IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. Please use instead of "if (IsWindowAppearing()) SetScrollHere()" to signify "default item". IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3af5399b..0893cba4 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2730,7 +2730,7 @@ struct ExampleAppConsole // Demonstrate keeping focus on the input box ImGui::SetItemDefaultFocus(); - if (reclaim_focus) //|| ImGui::IsItemHovered()) + if (reclaim_focus) ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget ImGui::End(); diff --git a/imgui_internal.h b/imgui_internal.h index c012f380..4cf1d7c6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -247,6 +247,16 @@ enum ImGuiDataType ImGuiDataType_Float2 }; +enum ImGuiDir +{ + ImGuiDir_None = -1, + ImGuiDir_Left = 0, + ImGuiDir_Right = 1, + ImGuiDir_Up = 2, + ImGuiDir_Down = 3, + ImGuiDir_Count_ +}; + enum ImGuiInputSource { ImGuiInputSource_None = 0, @@ -268,16 +278,6 @@ enum ImGuiInputReadMode ImGuiInputReadMode_RepeatFast }; -enum ImGuiDir -{ - ImGuiDir_None = -1, - ImGuiDir_Left = 0, - ImGuiDir_Right = 1, - ImGuiDir_Up = 2, - ImGuiDir_Down = 3, - ImGuiDir_Count_ -}; - enum ImGuiNavHighlightFlags_ { ImGuiNavHighlightFlags_TypeDefault = 1 << 0, From 5c83b55d0491f4349fbebc26b6b4add9f43febf4 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 15:49:35 +0100 Subject: [PATCH 301/319] Nav: Added ImGuiWindowFlags_NoNav shortcut, comments. (#787) --- imgui.h | 7 ++++--- imgui_demo.cpp | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/imgui.h b/imgui.h index 61ff2c69..cf45f1a5 100644 --- a/imgui.h +++ b/imgui.h @@ -551,11 +551,12 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, // Always show horizontal scrollbar (even if ContentSize.x < Size.x) ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // (WIP) Enable resize from any corners and borders. Your back-end needs to honor the different values of io.MouseCursor set by imgui. - ImGuiWindowFlags_NoNavFocus = 1 << 18, // No focusing of this window with gamepad/keyboard navigation - ImGuiWindowFlags_NoNavInputs = 1 << 19, // No gamepad/keyboard navigation within the window + ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window + ImGuiWindowFlags_NoNavFocus = 1 << 19, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB) + ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, // [Internal] - ImGuiWindowFlags_NavFlattened = 1 << 20, // (WIP) Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) + ImGuiWindowFlags_NavFlattened = 1 << 23, // (WIP) Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) ImGuiWindowFlags_ChildWindow = 1 << 24, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 0893cba4..01470918 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -184,7 +184,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (no_move) window_flags |= ImGuiWindowFlags_NoMove; if (no_resize) window_flags |= ImGuiWindowFlags_NoResize; if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; - if (no_nav) window_flags |= ImGuiWindowFlags_NoNavInputs; + if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; if (no_close) p_open = NULL; // Don't pass our bool* to Begin ImGui::SetNextWindowSize(ImVec2(550,680), ImGuiCond_FirstUseEver); @@ -1802,6 +1802,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Text("WantCaptureKeyboard: %d", io.WantCaptureKeyboard); ImGui::Text("WantTextInput: %d", io.WantTextInput); ImGui::Text("WantMoveMouse: %d", io.WantMoveMouse); + ImGui::Text("NavActive: %d, NavVisible: %d", io.NavActive, io.NavVisible); ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); ImGui::SameLine(); ShowHelpMarker("Request ImGui to render a mouse cursor for you in software. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); @@ -1827,7 +1828,6 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Text("Keys release:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyReleased(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); - ImGui::Text("NavActive: %d, NavVisible: %d", io.NavActive, io.NavVisible); ImGui::Text("NavInputs down:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputs[i] > 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f", i, io.NavInputs[i]); } ImGui::Text("NavInputs pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] == 0.0f) { ImGui::SameLine(); ImGui::Text("[%d]", i); } ImGui::Text("NavInputs duration:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f", i, io.NavInputsDownDuration[i]); } @@ -2432,7 +2432,7 @@ static void ShowExampleAppFixedOverlay(bool* p_open) ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f); ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); ImGui::SetNextWindowBgAlpha(0.3f); // Transparent background - if (ImGui::Begin("Example: Fixed Overlay", p_open, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_NoFocusOnAppearing|ImGuiWindowFlags_NoNavFocus|ImGuiWindowFlags_NoNavInputs)) + if (ImGui::Begin("Example: Fixed Overlay", p_open, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_NoFocusOnAppearing|ImGuiWindowFlags_NoNav)) { ImGui::Text("Simple overlay\nin the corner of the screen.\n(right-click to change position)"); ImGui::Separator(); From 92ee6b1185676a90bd7f805cdfe020b2deaaf927 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 16:02:21 +0100 Subject: [PATCH 302/319] Nav: Sets io.WantCaptureKeyboard when navigation is active. This is a little agressive but probably the best default and also a good way to get feedback. Added ImGuiNavFlags_NoCaptureKeyboard to disable this behavior. Comments. (#787) --- imgui.cpp | 6 +++++- imgui.h | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 436b559a..93547e96 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3347,7 +3347,7 @@ void ImGui::NewFrame() g.ModalWindowDarkeningRatio = 0.0f; } - // Update the WantCaptureMouse/WantCAptureKeyboard flags, so user can capture/discard the inputs away from the rest of their application. + // Update the WantCaptureMouse/WantCaptureKeyboard flags, so user can capture/discard the inputs away from the rest of their application. // When clicking outside of a window we assume the click is owned by the application and won't request capture. We need to track click ownership. int mouse_earliest_button_down = -1; bool mouse_any_down = false; @@ -3365,10 +3365,14 @@ void ImGui::NewFrame() g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0); else g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty()); + if (g.WantCaptureKeyboardNextFrame != -1) g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); else g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); + if (g.IO.NavActive && (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard) && !(g.IO.NavFlags & ImGuiNavFlags_NoCaptureKeyboard)) + g.IO.WantCaptureKeyboard = true; + g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : 0; g.MouseCursor = ImGuiMouseCursor_Arrow; g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1; diff --git a/imgui.h b/imgui.h index cf45f1a5..9aab0a7e 100644 --- a/imgui.h +++ b/imgui.h @@ -726,7 +726,8 @@ enum ImGuiNavInput_ ImGuiNavInput_PadTweakSlow, // slower tweaks // e.g. L-trigger, analog ImGuiNavInput_PadTweakFast, // faster tweaks // e.g. R-trigger, analog // Keyboard Mapping - // [BETA] You can map keyboard keys on the gamepad mapping for most inputs. Will add specialized keyboard mappings as we add features. + // [BETA] To use keyboard control you currently need to map keys to those gamepad inputs: PadActivate (Enter), PadCancel (Escape), PadInput (Enter). + // Will add specialized keyboard mappings as we add features and clarify the input interface. ImGuiNavInput_KeyMenu, // toggle menu // e.g. ALT ImGuiNavInput_KeyLeft, // move left // e.g. Arrow keys ImGuiNavInput_KeyRight, // move right @@ -740,7 +741,8 @@ enum ImGuiNavFlags_ { ImGuiNavFlags_EnableGamepad = 1 << 0, // Master gamepad navigation enable flag. This is mostly to instruct your imgui binding whether to fill in gamepad navigation inputs. ImGuiNavFlags_EnableKeyboard = 1 << 1, // Master keyboard navigation enable flag. This is mostly to instruct your imgui binding whether to fill in keyboard navigation inputs. - ImGuiNavFlags_MoveMouse = 1 << 2 // Request navigation to allow move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantMoveMouse=true. If enabled you MUST honor io.WantMoveMouse requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. + ImGuiNavFlags_MoveMouse = 1 << 2, // Request navigation to allow moving the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantMoveMouse=true. If enabled you MUST honor io.WantMoveMouse requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. + ImGuiNavFlags_NoCaptureKeyboard = 1 << 3 // Do not set the io.WantCaptureKeyboard flag with io.NavActive is set. }; // Enumeration for PushStyleColor() / PopStyleColor() From f35734c9250ec920a05b0328b5d157c3342027c6 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 17:02:30 +0100 Subject: [PATCH 303/319] Nav: Debugging code. (#787) --- imgui.cpp | 6 ++++++ imgui_internal.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 93547e96..58c879d6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2147,6 +2147,7 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) return false; const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) + g.NavScoringCount++; // We perform scoring on items bounding box clipped by their parent window on the other axis (clipping on our movement axis would give us equal scores for all clipped items) if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) @@ -2923,6 +2924,10 @@ static void ImGui::NavUpdate() ImGuiContext& g = *GImGui; g.IO.WantMoveMouse = false; +#if 0 + if (g.NavScoringCount > 0) printf("[%05d] NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); +#endif + // Process navigation init request (select first/default focus) if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitResultExplicit)) { @@ -3153,6 +3158,7 @@ static void ImGui::NavUpdate() g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous fabsf() calls in NavScoreItem(). //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] + g.NavScoringCount = 0; #if IMGUI_DEBUG_NAV_RECTS if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) g.OverlayDrawList.AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames <= 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredMousePos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); g.OverlayDrawList.AddCircleFilled(p, 3.0f, col); g.OverlayDrawList.AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } diff --git a/imgui_internal.h b/imgui_internal.h index 4cf1d7c6..027726e7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -615,6 +615,7 @@ struct ImGuiContext ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest) ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring. + int NavScoringCount; // Metrics for debugging ImGuiWindow* NavWindowingTarget; // When selecting a window (holding Menu+FocusPrev/Next, or equivalent of CTRL-TAB) this window is temporarily displayed front-most. float NavWindowingHighlightTimer; float NavWindowingHighlightAlpha; @@ -732,6 +733,7 @@ struct ImGuiContext NavId = NavActivateId = NavActivateDownId = NavInputId = 0; NavJustTabbedId = NavJustMovedToId = NavNextActivateId = 0; NavScoringRectScreen = ImRect(); + NavScoringCount = 0; NavWindowingTarget = NULL; NavWindowingHighlightTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; From fb7cf4a475f216dc9e41f68fbd75772d8ec8a0db Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 18:25:03 +0100 Subject: [PATCH 304/319] Nav: Removed old unnecessary ifdefs. --- imgui.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 58c879d6..b9e5b069 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7653,9 +7653,7 @@ bool ImGui::ArrowButton(ImGuiID id, ImGuiDir dir, ImVec2 padding, ImGuiButtonFla bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); -#ifdef IMGUI_HAS_NAV RenderNavHighlight(bb, id); -#endif RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); RenderTriangle(bb.Min + padding, dir, 1.0f); @@ -12011,9 +12009,7 @@ bool ImGui::SplitterBehavior(ImGuiID id, const ImRect& bb, ImGuiAxis axis, float ImGuiWindow* window = g.CurrentWindow; const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; -#ifdef IMGUI_HAS_NAV window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; -#endif bool add = ItemAdd(bb, id); window->DC.ItemFlags = item_flags_backup; if (!add) From e5e3cc617eacab072422aba91009cf4f3d0f47b1 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 19:07:26 +0100 Subject: [PATCH 305/319] Nav: Maintaining a NavActivatePressedId field that widget can conveniently use along with NavActivateDownId. --- imgui.cpp | 21 ++++++++++++--------- imgui_internal.h | 7 ++++--- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b9e5b069..4e8816ae 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3042,28 +3042,31 @@ static void ImGui::NavUpdate() } } - g.NavActivateId = g.NavActivateDownId = g.NavInputId = 0; - if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget) + // Process manual activation request + g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; + if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { - if (g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadActivate, ImGuiInputReadMode_Pressed)) + bool activate_down = IsNavInputDown(ImGuiNavInput_PadActivate); + bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_PadActivate, ImGuiInputReadMode_Pressed); + if (g.ActiveId == 0 && activate_pressed) g.NavActivateId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputDown(ImGuiNavInput_PadActivate)) + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) g.NavActivateDownId = g.NavId; + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) + g.NavActivatePressedId = g.NavId; + if (g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadInput, ImGuiInputReadMode_Pressed)) g.NavInputId = g.NavId; } if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) - { - g.NavActivateId = g.NavActivateDownId = g.NavInputId = 0; g.NavDisableHighlight = true; - } if (g.NavActivateId != 0) IM_ASSERT(g.NavActivateDownId == g.NavActivateId); g.NavMoveRequest = false; - // Process explicit activation request + // Process programmatic activation request if (g.NavNextActivateId != 0) - g.NavActivateId = g.NavActivateDownId = g.NavInputId = g.NavNextActivateId; + g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; g.NavNextActivateId = 0; // Initiate directional inputs request diff --git a/imgui_internal.h b/imgui_internal.h index 027726e7..785b6807 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -608,8 +608,9 @@ struct ImGuiContext // Navigation data (for gamepad/keyboard) ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' ImGuiID NavId; // Focused item for navigation - ImGuiID NavActivateId; // ~~ IsNavInputPressed(ImGuiNavInput_PadActivate) ? NavId : 0, also set when calling ActivateItem() - ImGuiID NavActivateDownId; // ~~ IsNavInputPressed(ImGuiNavInput_PadActivate) ? NavId : 0 + ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && IsNavInputPressed(ImGuiNavInput_PadActivate) ? NavId : 0, also set when calling ActivateItem() + ImGuiID NavActivateDownId; // ~~ IsNavInputDown(ImGuiNavInput_PadActivate) ? NavId : 0 + ImGuiID NavActivatePressedId; // ~~ IsNavInputPressed(ImGuiNavInput_PadActivate) ? NavId : 0 ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_PadInput) ? NavId : 0 ImGuiID NavJustTabbedId; // Just tabbed to this id. ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame @@ -730,7 +731,7 @@ struct ImGuiContext NextTreeNodeOpenCond = 0; NavWindow = NULL; - NavId = NavActivateId = NavActivateDownId = NavInputId = 0; + NavId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0; NavJustTabbedId = NavJustMovedToId = NavNextActivateId = 0; NavScoringRectScreen = ImRect(); NavScoringCount = 0; From 4932303e62e529f4b12eca97b2b0466d30824351 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 20:15:31 +0100 Subject: [PATCH 306/319] Nav: DragBehavior: Tweaks (to take the noise out of the next commit). --- imgui.cpp | 132 +++++++++++++++++++++++++++--------------------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4e8816ae..ae6c9c29 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8852,73 +8852,73 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s // Process clicking on the drag if (g.ActiveId == id) { - if (g.IO.MouseDown[0] || g.NavActivateDownId == id) - { - if (g.ActiveIdIsJustActivated) - { - // Lock current value on click - g.DragCurrentValue = *v; - g.DragLastMouseDelta = ImVec2(0.f, 0.f); - } - - if (v_speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX) - v_speed = (v_max - v_min) * g.DragSpeedDefaultRatio; - - float v_cur = g.DragCurrentValue; - const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f); - float adjust_delta = 0.0f; - if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid()) - { - adjust_delta = mouse_drag_delta.x - g.DragLastMouseDelta.x; - if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f) - adjust_delta *= g.DragSpeedScaleFast; - if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f) - adjust_delta *= g.DragSpeedScaleSlow; - g.DragLastMouseDelta.x = mouse_drag_delta.x; - } - if (g.ActiveIdSource == ImGuiInputSource_Nav) - { - adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard|ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; - if (v_min < v_max && ((v_cur >= v_max && adjust_delta > 0.0f) || (v_cur <= v_min && adjust_delta < 0.0f))) // This is to avoid applying the saturation when already past the limits - adjust_delta = 0.0f; - v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); - } - adjust_delta *= v_speed; - - if (fabsf(adjust_delta) > 0.0f) - { - if (fabsf(power - 1.0f) > 0.001f) - { - // Logarithmic curve on both side of 0.0 - float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur; - float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f; - float v1 = powf(v0_abs, 1.0f / power) + (adjust_delta * v0_sign); - float v1_abs = v1 >= 0.0f ? v1 : -v1; - float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f; // Crossed sign line - v_cur = powf(v1_abs, power) * v0_sign * v1_sign; // Reapply sign - } - else - { - v_cur += adjust_delta; - } - - // Clamp - if (v_min < v_max) - v_cur = ImClamp(v_cur, v_min, v_max); - g.DragCurrentValue = v_cur; - } - - // Round to user desired precision, then apply - v_cur = RoundScalar(v_cur, decimal_precision); - if (*v != v_cur) - { - *v = v_cur; - value_changed = true; - } - } - else - { + if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) ClearActiveID(); + else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivateDownId != id) + ClearActiveID(); + } + if (g.ActiveId == id) + { + if (g.ActiveIdIsJustActivated) + { + // Lock current value on click + g.DragCurrentValue = *v; + g.DragLastMouseDelta = ImVec2(0.f, 0.f); + } + + if (v_speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX) + v_speed = (v_max - v_min) * g.DragSpeedDefaultRatio; + + float v_cur = g.DragCurrentValue; + const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f); + float adjust_delta = 0.0f; + if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid()) + { + adjust_delta = mouse_drag_delta.x - g.DragLastMouseDelta.x; + if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f) + adjust_delta *= g.DragSpeedScaleFast; + if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f) + adjust_delta *= g.DragSpeedScaleSlow; + g.DragLastMouseDelta.x = mouse_drag_delta.x; + } + if (g.ActiveIdSource == ImGuiInputSource_Nav) + { + adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard|ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; + if (v_min < v_max && ((v_cur >= v_max && adjust_delta > 0.0f) || (v_cur <= v_min && adjust_delta < 0.0f))) // This is to avoid applying the saturation when already past the limits + adjust_delta = 0.0f; + v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); + } + adjust_delta *= v_speed; + + if (fabsf(adjust_delta) > 0.0f) + { + if (fabsf(power - 1.0f) > 0.001f) + { + // Logarithmic curve on both side of 0.0 + float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur; + float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f; + float v1 = powf(v0_abs, 1.0f / power) + (adjust_delta * v0_sign); + float v1_abs = v1 >= 0.0f ? v1 : -v1; + float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f; // Crossed sign line + v_cur = powf(v1_abs, power) * v0_sign * v1_sign; // Reapply sign + } + else + { + v_cur += adjust_delta; + } + + // Clamp + if (v_min < v_max) + v_cur = ImClamp(v_cur, v_min, v_max); + g.DragCurrentValue = v_cur; + } + + // Round to user desired precision, then apply + v_cur = RoundScalar(v_cur, decimal_precision); + if (*v != v_cur) + { + *v = v_cur; + value_changed = true; } } From fb9fecea5ecf3759d33b2a5f1d439b6cd12d8f98 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 20:16:38 +0100 Subject: [PATCH 307/319] Nav: Sliders and Drags are toggle activated instead of requiring user to cross Cross/Space. (#787) --- imgui.cpp | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ae6c9c29..bb78b9a5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3054,8 +3054,7 @@ static void ImGui::NavUpdate() g.NavActivateDownId = g.NavId; if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) g.NavActivatePressedId = g.NavId; - - if (g.ActiveId == 0 && IsNavInputPressed(ImGuiNavInput_PadInput, ImGuiInputReadMode_Pressed)) + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_PadInput, ImGuiInputReadMode_Pressed)) g.NavInputId = g.NavId; } if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) @@ -8512,25 +8511,36 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f; } - // Process clicking on the slider + // Process interacting with the slider bool value_changed = false; if (g.ActiveId == id) { bool set_new_value = false; float clicked_t = 0.0f; - if (g.ActiveIdSource == ImGuiInputSource_Mouse && g.IO.MouseDown[0]) + if (g.ActiveIdSource == ImGuiInputSource_Mouse) { - const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; - clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f; - if (!is_horizontal) - clicked_t = 1.0f - clicked_t; - set_new_value = true; + if (!g.IO.MouseDown[0]) + { + ClearActiveID(); + } + else + { + const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; + clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f; + if (!is_horizontal) + clicked_t = 1.0f - clicked_t; + set_new_value = true; + } } - else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivateDownId == id) + else if (g.ActiveIdSource == ImGuiInputSource_Nav) { - const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard|ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f); + const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f); float delta = is_horizontal ? delta2.x : -delta2.y; - if (delta != 0.0f) + if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) + { + ClearActiveID(); + } + else if (delta != 0.0f) { clicked_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos); if (decimal_precision == 0 && !is_non_linear) @@ -8555,10 +8565,6 @@ bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v clicked_t = ImSaturate(clicked_t + delta); } } - else - { - ClearActiveID(); - } if (set_new_value) { @@ -8651,11 +8657,12 @@ bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, c // Tabbing or CTRL-clicking on Slider turns it into an input box bool start_text_input = false; const bool tab_focus_requested = FocusableItemRegister(window, id); - if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) + if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); if (tab_focus_requested || g.IO.KeyCtrl || g.NavInputId == id) { start_text_input = true; @@ -8708,6 +8715,7 @@ bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); } // Actual slider behavior + render grab @@ -8849,12 +8857,12 @@ bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_s bool value_changed = false; - // Process clicking on the drag + // Process interacting with the drag if (g.ActiveId == id) { if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) ClearActiveID(); - else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivateDownId != id) + else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) ClearActiveID(); } if (g.ActiveId == id) @@ -8956,11 +8964,12 @@ bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, f // Tabbing or CTRL-clicking on Drag turns it into an input box bool start_text_input = false; const bool tab_focus_requested = FocusableItemRegister(window, id); - if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || g.NavInputId == id) + if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id) { start_text_input = true; From 73d493c780443ca72f312df7aa7c4c76aefe4e12 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 20:34:11 +0100 Subject: [PATCH 308/319] Examples: Organized header files to make available features more visible. --- examples/allegro5_example/imgui_impl_a5.cpp | 10 +++++----- examples/allegro5_example/imgui_impl_a5.h | 9 ++++++--- examples/directx10_example/imgui_impl_dx10.cpp | 4 +++- examples/directx10_example/imgui_impl_dx10.h | 4 +++- examples/directx11_example/imgui_impl_dx11.cpp | 5 ++++- examples/directx11_example/imgui_impl_dx11.h | 5 ++++- examples/directx9_example/imgui_impl_dx9.cpp | 4 +++- examples/directx9_example/imgui_impl_dx9.h | 4 +++- examples/marmalade_example/imgui_impl_marmalade.cpp | 4 +++- examples/marmalade_example/imgui_impl_marmalade.h | 4 +++- examples/opengl2_example/imgui_impl_glfw_gl2.cpp | 4 +++- examples/opengl2_example/imgui_impl_glfw_gl2.h | 4 +++- examples/opengl3_example/imgui_impl_glfw_gl3.cpp | 6 +++++- examples/opengl3_example/imgui_impl_glfw_gl3.h | 6 +++++- examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp | 4 +++- examples/sdl_opengl2_example/imgui_impl_sdl_gl2.h | 4 +++- examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp | 4 +++- examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h | 4 +++- examples/vulkan_example/imgui_impl_glfw_vulkan.cpp | 4 +++- examples/vulkan_example/imgui_impl_glfw_vulkan.h | 4 +++- 20 files changed, 71 insertions(+), 26 deletions(-) diff --git a/examples/allegro5_example/imgui_impl_a5.cpp b/examples/allegro5_example/imgui_impl_a5.cpp index 42691362..1eb118e6 100644 --- a/examples/allegro5_example/imgui_impl_a5.cpp +++ b/examples/allegro5_example/imgui_impl_a5.cpp @@ -1,14 +1,14 @@ // ImGui Allegro 5 bindings -// In this binding, ImTextureID is used to store a 'ALLEGRO_BITMAP*' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. -// TODO: -// - Clipboard is not supported. +// Implemented features: +// [X] User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// Missing features: +// [ ] Clipboard support via al_set_clipboard_text/al_clipboard_has_text. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui -// by @birthggd +// https://github.com/ocornut/imgui, Original code by @birthggd #include // uint64_t #include // memcpy diff --git a/examples/allegro5_example/imgui_impl_a5.h b/examples/allegro5_example/imgui_impl_a5.h index b7439fef..7f164435 100644 --- a/examples/allegro5_example/imgui_impl_a5.h +++ b/examples/allegro5_example/imgui_impl_a5.h @@ -1,11 +1,14 @@ // ImGui Allegro 5 bindings -// In this binding, ImTextureID is used to store a 'ALLEGRO_BITMAP*' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. + +// Implemented features: +// [X] User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// Missing features: +// [ ] Clipboard support via al_set_clipboard_text/al_clipboard_has_text. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui -// by @birthggd +// https://github.com/ocornut/imgui, Original code by @birthggd #pragma once diff --git a/examples/directx10_example/imgui_impl_dx10.cpp b/examples/directx10_example/imgui_impl_dx10.cpp index 14a47c4a..e3080f9c 100644 --- a/examples/directx10_example/imgui_impl_dx10.cpp +++ b/examples/directx10_example/imgui_impl_dx10.cpp @@ -1,5 +1,7 @@ // ImGui Win32 + DirectX10 binding -// In this binding, ImTextureID is used to store a 'ID3D10ShaderResourceView*' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. + +// Implemented features: +// [X] User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/directx10_example/imgui_impl_dx10.h b/examples/directx10_example/imgui_impl_dx10.h index 38a03a27..604b7b63 100644 --- a/examples/directx10_example/imgui_impl_dx10.h +++ b/examples/directx10_example/imgui_impl_dx10.h @@ -1,5 +1,7 @@ // ImGui Win32 + DirectX10 binding -// In this binding, ImTextureID is used to store a 'ID3D10ShaderResourceView*' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. + +// Implemented features: +// [X] User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/directx11_example/imgui_impl_dx11.cpp b/examples/directx11_example/imgui_impl_dx11.cpp index 67b8a9ee..fbcfd953 100644 --- a/examples/directx11_example/imgui_impl_dx11.cpp +++ b/examples/directx11_example/imgui_impl_dx11.cpp @@ -1,5 +1,8 @@ // ImGui Win32 + DirectX11 binding -// In this binding, ImTextureID is used to store a 'ID3D11ShaderResourceView*' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. + +// Implemented features: +// [X] User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Keyboard navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableKeyboard'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/directx11_example/imgui_impl_dx11.h b/examples/directx11_example/imgui_impl_dx11.h index 36066746..fc2b6bdb 100644 --- a/examples/directx11_example/imgui_impl_dx11.h +++ b/examples/directx11_example/imgui_impl_dx11.h @@ -1,5 +1,8 @@ // ImGui Win32 + DirectX11 binding -// In this binding, ImTextureID is used to store a 'ID3D11ShaderResourceView*' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. + +// Implemented features: +// [X] User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Keyboard navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableKeyboard'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/directx9_example/imgui_impl_dx9.cpp b/examples/directx9_example/imgui_impl_dx9.cpp index b47a840b..8e0808fd 100644 --- a/examples/directx9_example/imgui_impl_dx9.cpp +++ b/examples/directx9_example/imgui_impl_dx9.cpp @@ -1,5 +1,7 @@ // ImGui Win32 + DirectX9 binding -// In this binding, ImTextureID is used to store a 'LPDIRECT3DTEXTURE9' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. + +// Implemented features: +// [X] User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/directx9_example/imgui_impl_dx9.h b/examples/directx9_example/imgui_impl_dx9.h index acaed531..41fa743d 100644 --- a/examples/directx9_example/imgui_impl_dx9.h +++ b/examples/directx9_example/imgui_impl_dx9.h @@ -1,5 +1,7 @@ // ImGui Win32 + DirectX9 binding -// In this binding, ImTextureID is used to store a 'LPDIRECT3DTEXTURE9' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. + +// Implemented features: +// [X] User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/marmalade_example/imgui_impl_marmalade.cpp b/examples/marmalade_example/imgui_impl_marmalade.cpp index 0bffb30d..e8d60af9 100644 --- a/examples/marmalade_example/imgui_impl_marmalade.cpp +++ b/examples/marmalade_example/imgui_impl_marmalade.cpp @@ -1,5 +1,7 @@ // ImGui Marmalade binding with IwGx -// In this binding, ImTextureID is used to store a 'CIwTexture*' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. + +// Implemented features: +// [X] User texture binding. Use 'CIwTexture*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/marmalade_example/imgui_impl_marmalade.h b/examples/marmalade_example/imgui_impl_marmalade.h index 5342cb7e..79b5e50a 100644 --- a/examples/marmalade_example/imgui_impl_marmalade.h +++ b/examples/marmalade_example/imgui_impl_marmalade.h @@ -1,5 +1,7 @@ // ImGui Marmalade binding with IwGx -// In this binding, ImTextureID is used to store a 'CIwTexture*' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. + +// Implemented features: +// [X] User texture binding. Use 'CIwTexture*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/opengl2_example/imgui_impl_glfw_gl2.cpp b/examples/opengl2_example/imgui_impl_glfw_gl2.cpp index 27052b60..954fbcde 100644 --- a/examples/opengl2_example/imgui_impl_glfw_gl2.cpp +++ b/examples/opengl2_example/imgui_impl_glfw_gl2.cpp @@ -1,7 +1,9 @@ // ImGui GLFW binding with OpenGL (legacy, fixed pipeline) -// In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. // (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) +// Implemented features: +// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. + // **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** // **Prefer using the code in the opengl3_example/ folder** // This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. diff --git a/examples/opengl2_example/imgui_impl_glfw_gl2.h b/examples/opengl2_example/imgui_impl_glfw_gl2.h index d04a84fa..91d1e087 100644 --- a/examples/opengl2_example/imgui_impl_glfw_gl2.h +++ b/examples/opengl2_example/imgui_impl_glfw_gl2.h @@ -1,7 +1,9 @@ // ImGui GLFW binding with OpenGL (legacy, fixed pipeline) -// In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. // (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) +// Implemented features: +// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. + // **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** // **Prefer using the code in the opengl3_example/ folder** // See imgui_impl_glfw.cpp for details. diff --git a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp index 0b8e5d83..dcd40772 100644 --- a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp +++ b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp @@ -1,8 +1,12 @@ // ImGui GLFW binding with OpenGL3 + shaders -// In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. // (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) +// Implemented features: +// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Keyboard navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableKeyboard'. +// [X] Gamepad navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableGamepad'. + // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. diff --git a/examples/opengl3_example/imgui_impl_glfw_gl3.h b/examples/opengl3_example/imgui_impl_glfw_gl3.h index 5f304398..e7a4c2f6 100644 --- a/examples/opengl3_example/imgui_impl_glfw_gl3.h +++ b/examples/opengl3_example/imgui_impl_glfw_gl3.h @@ -1,8 +1,12 @@ // ImGui GLFW binding with OpenGL3 + shaders -// In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. // (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) +// Implemented features: +// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Keyboard navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableKeyboard'. +// [X] Gamepad navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableGamepad'. + // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. diff --git a/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp b/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp index 801c433f..83597d06 100644 --- a/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp +++ b/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp @@ -1,7 +1,9 @@ // ImGui SDL2 binding with OpenGL (legacy, fixed pipeline) -// In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. // (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) +// Implemented features: +// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. + // **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** // **Prefer using the code in the sdl_opengl3_example/ folder** // This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. diff --git a/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.h b/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.h index 32d7bc0e..915ae52d 100644 --- a/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.h +++ b/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.h @@ -1,7 +1,9 @@ // ImGui SDL2 binding with OpenGL (legacy, fixed pipeline) -// In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. // (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) +// Implemented features: +// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. + // **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** // **Prefer using the code in the sdl_opengl3_example/ folder** // See imgui_impl_sdl.cpp for details. diff --git a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp index 35d9cdde..c94bcdea 100644 --- a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp +++ b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp @@ -1,8 +1,10 @@ // ImGui SDL2 binding with OpenGL3 -// In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. // (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) +// Implemented features: +// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. + // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. diff --git a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h index 54381866..3f76e294 100644 --- a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h +++ b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h @@ -1,8 +1,10 @@ // ImGui SDL2 binding with OpenGL3 -// In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. // (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) +// Implemented features: +// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. + // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. diff --git a/examples/vulkan_example/imgui_impl_glfw_vulkan.cpp b/examples/vulkan_example/imgui_impl_glfw_vulkan.cpp index 3748b504..3edf2097 100644 --- a/examples/vulkan_example/imgui_impl_glfw_vulkan.cpp +++ b/examples/vulkan_example/imgui_impl_glfw_vulkan.cpp @@ -1,5 +1,7 @@ // ImGui GLFW binding with Vulkan + shaders -// FIXME: Changes of ImTextureID aren't supported by this binding! Please, someone add it! + +// Missing features: +// [ ] User texture binding. Changes of ImTextureID aren't supported by this binding! See https://github.com/ocornut/imgui/pull/914 // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 5 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXX_CreateFontsTexture(), ImGui_ImplXXXX_NewFrame(), ImGui_ImplXXXX_Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/vulkan_example/imgui_impl_glfw_vulkan.h b/examples/vulkan_example/imgui_impl_glfw_vulkan.h index 0e5c96a8..766a9595 100644 --- a/examples/vulkan_example/imgui_impl_glfw_vulkan.h +++ b/examples/vulkan_example/imgui_impl_glfw_vulkan.h @@ -1,5 +1,7 @@ // ImGui GLFW binding with Vulkan + shaders -// FIXME: Changes of ImTextureID aren't supported by this binding! Please, someone add it! + +// Missing features: +// [ ] User texture binding. Changes of ImTextureID aren't supported by this binding! See https://github.com/ocornut/imgui/pull/914 // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 5 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXX_CreateFontsTexture(), ImGui_ImplXXXX_NewFrame(), ImGui_ImplXXXX_Render() and ImGui_ImplXXXX_Shutdown(). From 4b49f03a40a4aadfa8d586c184d098eda539f5bf Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 23:16:40 +0100 Subject: [PATCH 309/319] Examples: SDL+GL3: Added Navigation keyboard mapping. (#787) --- .../imgui_impl_sdl_gl3.cpp | 24 +++++++++++++++++++ .../sdl_opengl3_example/imgui_impl_sdl_gl3.h | 1 + examples/sdl_opengl3_example/main.cpp | 3 ++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp index 06d62867..0619b6ad 100644 --- a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp +++ b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp @@ -4,6 +4,7 @@ // Implemented features: // [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Keyboard navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableKeyboard'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). @@ -412,6 +413,29 @@ void ImGui_ImplSdlGL3_NewFrame(SDL_Window* window) // Hide OS mouse cursor if ImGui is drawing it SDL_ShowCursor(io.MouseDrawCursor ? 0 : 1); + // Gamepad/keyboard navigation mapping [BETA] + memset(io.NavInputs, 0, sizeof(io.NavInputs)); + if (io.NavFlags & ImGuiNavFlags_EnableKeyboard) + { + // Update keyboard + // FIXME-NAV: We are still using some of the ImGuiNavInput_PadXXX enums as keyboard support is incomplete. + #define MAP_KEY(NAV_NO, KEY_NO) { if (io.KeysDown[KEY_NO]) io.NavInputs[NAV_NO] = 1.0f; } + MAP_KEY(ImGuiNavInput_KeyLeft, SDL_SCANCODE_LEFT); + MAP_KEY(ImGuiNavInput_KeyRight, SDL_SCANCODE_RIGHT); + MAP_KEY(ImGuiNavInput_KeyUp, SDL_SCANCODE_UP); + MAP_KEY(ImGuiNavInput_KeyDown, SDL_SCANCODE_DOWN); + MAP_KEY(ImGuiNavInput_KeyMenu, SDL_SCANCODE_LALT); + MAP_KEY(ImGuiNavInput_KeyMenu, SDL_SCANCODE_RALT); + MAP_KEY(ImGuiNavInput_PadActivate, SDL_SCANCODE_SPACE); + MAP_KEY(ImGuiNavInput_PadCancel, SDL_SCANCODE_ESCAPE); + MAP_KEY(ImGuiNavInput_PadInput, SDL_SCANCODE_RETURN); + MAP_KEY(ImGuiNavInput_PadTweakSlow, SDL_SCANCODE_LALT); + MAP_KEY(ImGuiNavInput_PadTweakSlow, SDL_SCANCODE_LALT); + MAP_KEY(ImGuiNavInput_PadTweakFast, SDL_SCANCODE_LSHIFT); + MAP_KEY(ImGuiNavInput_PadTweakFast, SDL_SCANCODE_RSHIFT); + #undef MAP_KEY + } + // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. ImGui::NewFrame(); } diff --git a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h index 3f76e294..fc7e1e96 100644 --- a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h +++ b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h @@ -4,6 +4,7 @@ // Implemented features: // [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Keyboard navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableKeyboard'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index 4bab4916..7cd4ca98 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -34,7 +34,9 @@ int main(int, char**) gl3wInit(); // Setup ImGui binding + ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui_ImplSdlGL3_Init(window); + //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Setup style ImGui::StyleColorsDark(); @@ -47,7 +49,6 @@ int main(int, char**) // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'misc/fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //ImGuiIO& io = ImGui::GetIO(); //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); From 0c0d811859aeede5d0dca5d3d7e5026b8d5ee5a8 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Feb 2018 23:53:40 +0100 Subject: [PATCH 310/319] Nav: SetItemDefaultFocus() doesn't make the navigation cursorr highlight visible. Renamed NavInitResultExplicit to NavInitRequestFromMove. (#787) --- TODO.txt | 1 + imgui.cpp | 11 +++++------ imgui_internal.h | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/TODO.txt b/TODO.txt index 6f6ab7cc..fe9b6cce 100644 --- a/TODO.txt +++ b/TODO.txt @@ -231,6 +231,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - font: (api breaking) removed "TTF" from symbol names. also because it now supports OTF. - nav: integrate navigation branch into master. (#787) + - nav: allow input system to be be more tolerant of io.DeltaTime=0.0f - nav: Left within a tree node block as a fallback. - nav: Esc on a flattened child - nav: menus: allow pressing Menu to leave a sub-menu. diff --git a/imgui.cpp b/imgui.cpp index bb78b9a5..a63073b9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2656,8 +2656,8 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) { SetNavID(0, g.NavLayer); g.NavInitRequest = true; + g.NavInitRequestFromMove = false; g.NavInitResultId = 0; - g.NavInitResultExplicit = false; g.NavInitResultRectRel = ImRect(); NavUpdateAnyRequestFlag(); } @@ -2929,18 +2929,18 @@ static void ImGui::NavUpdate() #endif // Process navigation init request (select first/default focus) - if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitResultExplicit)) + if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove)) { // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) IM_ASSERT(g.NavWindow); - if (g.NavInitResultExplicit) + if (g.NavInitRequestFromMove) SetNavIDAndMoveMouse(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel); else SetNavID(g.NavInitResultId, g.NavLayer); g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel; } g.NavInitRequest = false; - g.NavInitResultExplicit = false; + g.NavInitRequestFromMove = false; g.NavInitResultId = 0; g.NavJustMovedToId = 0; @@ -3098,7 +3098,7 @@ static void ImGui::NavUpdate() // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match if (g.NavMoveRequest && g.NavId == 0) { - g.NavInitRequest = g.NavInitResultExplicit = true; + g.NavInitRequest = g.NavInitRequestFromMove = true; g.NavInitResultId = 0; g.NavDisableHighlight = false; } @@ -7147,7 +7147,6 @@ void ImGui::SetItemDefaultFocus() if (g.NavWindow == window->NavRootWindow && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent) { g.NavInitRequest = false; - g.NavInitResultExplicit = true; g.NavInitResultId = g.NavWindow->DC.LastItemId; g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); NavUpdateAnyRequestFlag(); diff --git a/imgui_internal.h b/imgui_internal.h index 785b6807..d2183b92 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -630,9 +630,9 @@ struct ImGuiContext bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest bool NavInitRequest; // Init request for appearing window to select first item + bool NavInitRequestFromMove; ImGuiID NavInitResultId; ImRect NavInitResultRectRel; - bool NavInitResultExplicit; // Whether the result was explicitly requested with SetItemDefaultFocus() bool NavMoveFromClampedRefRect; // Set by manual scrolling, if we scroll to a point where NavId isn't visible we reset navigation from visible items bool NavMoveRequest; // Move request for this frame ImGuiNavForward NavMoveRequestForward; // None / ForwardQueued / ForwardActive (this is used to navigate sibling parent menus from a child menu) @@ -747,8 +747,8 @@ struct ImGuiContext NavDisableMouseHover = false; NavAnyRequest = false; NavInitRequest = false; + NavInitRequestFromMove = false; NavInitResultId = 0; - NavInitResultExplicit = false; NavMoveFromClampedRefRect = false; NavMoveRequest = false; NavMoveRequestForward = ImGuiNavForward_None; From ce2b8d3255fb2ff66b8ab924bf2e9022c8385740 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Feb 2018 13:16:44 +0100 Subject: [PATCH 311/319] Nav: Comments, removed extraneous parameter. --- imgui.cpp | 2 +- imgui_internal.h | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a63073b9..2890117b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8701,7 +8701,7 @@ bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); ItemSize(bb, style.FramePadding.y); - if (!ItemAdd(frame_bb, id, &frame_bb)) + if (!ItemAdd(frame_bb, id)) return false; const bool hovered = ItemHoverable(frame_bb, id); diff --git a/imgui_internal.h b/imgui_internal.h index d2183b92..e17e986e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -625,7 +625,7 @@ struct ImGuiContext int NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid - bool NavMousePosDirty; // When set we will update mouse position if (NavFlags & ImGuiNavFlags_MoveMouse) if set + bool NavMousePosDirty; // When set we will update mouse position if (NavFlags & ImGuiNavFlags_MoveMouse) if set (NB: this not enabled by default) bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (nb: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest @@ -636,8 +636,7 @@ struct ImGuiContext bool NavMoveFromClampedRefRect; // Set by manual scrolling, if we scroll to a point where NavId isn't visible we reset navigation from visible items bool NavMoveRequest; // Move request for this frame ImGuiNavForward NavMoveRequestForward; // None / ForwardQueued / ForwardActive (this is used to navigate sibling parent menus from a child menu) - ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down) - ImGuiDir NavMoveDirLast; // Direction of the previous move request + ImGuiDir NavMoveDir, NavMoveDirLast; // Direction of the move request (left/right/up/down), direction of the previous move request ImGuiNavMoveResult NavMoveResultLocal; // Best move request candidate within NavWindow ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using the NavFlattened flag) From ad093966726d662359398de313dbb8e2bc1053bd Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Feb 2018 16:32:51 +0100 Subject: [PATCH 312/319] ImVec2: added [] operator. This is becoming desirable for some types of code, better added sooner than later. --- imgui.h | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.h b/imgui.h index 9aab0a7e..4644a8ad 100644 --- a/imgui.h +++ b/imgui.h @@ -116,6 +116,7 @@ struct ImVec2 float x, y; ImVec2() { x = y = 0.0f; } ImVec2(float _x, float _y) { x = _x; y = _y; } + float operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1); return *(&x + idx); } // We very rarely use this [] operator, thus an assert is fine. #ifdef IM_VEC2_CLASS_EXTRA // Define constructor and implicit cast operators in imconfig.h to convert back<>forth from your math types and ImVec2. IM_VEC2_CLASS_EXTRA #endif From ed4bbc4fd4c7041f98f204ee695cfe441032a9f7 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Feb 2018 18:26:42 +0100 Subject: [PATCH 313/319] Nav: Comments, guides. --- TODO.txt | 1 + imgui.cpp | 18 +++++++++--------- imgui.h | 36 ++++++++++++++++++------------------ 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/TODO.txt b/TODO.txt index fe9b6cce..939393ba 100644 --- a/TODO.txt +++ b/TODO.txt @@ -238,6 +238,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav: integrate/design keyboard controls. - nav: simulate right-click or context activation? (SHIFT+F10) - nav: tabs should go through most/all widgets (in submission order?). + - nav: when CTRL-Tab/windowing is active, the HoveredWindow detection doesn't take account of the window display re-ordering. - nav: cannot access menubar of a flattened child window with Alt/menu key (not a very common use case..). - nav: esc/enter default behavior for popups, e.g. be able to mark an "ok" or "cancel" button that would get triggered by those keys. - nav: when activating a button that changes label (without a static ID) or disappear, can we somehow automatically recover into a nearest highlight item? diff --git a/imgui.cpp b/imgui.cpp index c2e96ea2..6d485418 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -80,7 +80,8 @@ - ESCAPE to revert text to its original value. - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!) - Controls are automatically adjusted for OSX to match standard OSX text editing operations. - - Gamepad/keyboard navigation are in beta-phase, see Programmer Guide below. + - Gamepad navigation: see suggested mappings in imgui.h ImGuiNavInput_ + - Keyboard navigation: see suggested mappings in imgui.h ImGuiNavInput_ PROGRAMMER GUIDE @@ -211,8 +212,7 @@ USING GAMEPAD/KEYBOARD NAVIGATION [BETA] - - Gamepad/keyboard navigation support is now available. Your feedback and bug reports are greatly welcome! - - See https://github.com/ocornut/imgui/issues/787 discussion thread and ask questions there. + - Ask questions and report issues at https://github.com/ocornut/imgui/issues/787. - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. - Your inputs are passed to imgui by filling the io.NavInputs[] array. See 'enum ImGuiNavInput_' in imgui.h for a description of available inputs. - Please refer to the examples/ application for suggested keyboard and gamepad mapping. @@ -221,12 +221,12 @@ - The ImGuiNavFlags_EnableGamepad and ImGuiNavFlags_EnableKeyboard flags of io.NavFlags are only here to instruct your binding whether to find inputs. - For gamepad use, the easiest approach is to go all-or-nothing, with a buttons combo that toggle your inputs between imgui and your game/application. Sharing inputs in a more advanced or granular way between imgui and your game/application may be tricky and requires further work on imgui. + When keyboard navigation is active (io.NavActive + NavFlags_EnableKeyboard), the io.WantCaptureKeyboard is set. For more advanced uses, you may want to use: - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). - query focus information with e.g. IsWindowFocused(), IsItemFocused() etc. functions. - The reality is more complex than what those flags can express. Please discuss your issues and usage scenario in the thread above! - As we head toward more keyboard-oriented development this aspect will need to be improved. + Please reach out if you think the game vs navigation input sharing could be improved. - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiNavFlags_MoveMouse flag in io.NavFlags. Enabling ImGuiNavFlags_MoveMouse instructs dear imgui to move your mouse cursor along with navigation movement. When enabled, the NewFrame() functions may alter 'io.MousePos' and set 'io.WantMoveMouse' to notify you that it did so. @@ -2675,7 +2675,7 @@ static ImVec2 NavCalcPreferredMousePos() const ImRect& rect_rel = window->NavRectRel[g.NavLayer]; ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); ImRect visible_rect = GetViewportRect(); - return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. + return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta. } static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N) @@ -2823,7 +2823,7 @@ static void ImGui::NavUpdateWindowing() } // Keyboard: Press and Release ALT to toggle menu layer - // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of backend clearing releases all keys on ALT-TAB + // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu, ImGuiInputReadMode_Released)) if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) apply_toggle_layer = true; @@ -4415,7 +4415,7 @@ int ImGui::GetKeyIndex(ImGuiKey imgui_key) return GImGui->IO.KeyMap[imgui_key]; } -// Note that imgui doesn't know the semantic of each entry of io.KeyDown[]. Use your own indices/enums according to how your backend/engine stored them into KeyDown[]! +// Note that imgui doesn't know the semantic of each entry of io.KeyDown[]. Use your own indices/enums according to how your back-end/engine stored them into KeyDown[]! bool ImGui::IsKeyDown(int user_key_index) { if (user_key_index < 0) return false; @@ -4536,7 +4536,7 @@ bool ImGui::IsMousePosValid(const ImVec2* mouse_pos) return mouse_pos->x >= MOUSE_INVALID && mouse_pos->y >= MOUSE_INVALID; } -// NB: This is only valid if IsMousePosValid(). Backends in theory should always keep mouse position valid when dragging even outside the client window. +// NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window. ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold) { ImGuiContext& g = *GImGui; diff --git a/imgui.h b/imgui.h index 4644a8ad..136f296e 100644 --- a/imgui.h +++ b/imgui.h @@ -710,27 +710,27 @@ enum ImGuiKey_ enum ImGuiNavInput_ { // Gamepad Mapping - ImGuiNavInput_PadActivate, // press button, tweak value // e.g. Circle button - ImGuiNavInput_PadCancel, // close menu/popup/child, lose selection // e.g. Cross button - ImGuiNavInput_PadInput, // text input // e.g. Triangle button - ImGuiNavInput_PadMenu, // toggle menu, hold to: focus, move, resize // e.g. Square button - ImGuiNavInput_PadDpadLeft, // move left, resize window (with PadMenu) // e.g. D-pad directions - ImGuiNavInput_PadDpadRight, // move right - ImGuiNavInput_PadDpadUp, // move up - ImGuiNavInput_PadDpadDown, // move down - ImGuiNavInput_PadLStickLeft, // scroll up, move window (with PadMenu) // e.g. left stick directions (analog) - ImGuiNavInput_PadLStickRight, // scroll right - ImGuiNavInput_PadLStickUp, // scroll up - ImGuiNavInput_PadLStickDown, // scroll down - ImGuiNavInput_PadFocusPrev, // next window (with PadMenu) // e.g. L-trigger - ImGuiNavInput_PadFocusNext, // prev window (with PadMenu) // e.g. R-trigger - ImGuiNavInput_PadTweakSlow, // slower tweaks // e.g. L-trigger, analog - ImGuiNavInput_PadTweakFast, // faster tweaks // e.g. R-trigger, analog + ImGuiNavInput_PadActivate, // activate / open / toggle / tweak value // e.g. Circle (PS4), A (Xbox), B (Switch) + ImGuiNavInput_PadCancel, // cancel / close / exit // e.g. Cross (PS4), B (Xbox), A (Switch) + ImGuiNavInput_PadInput, // text input / on-screen keyboard // e.g. Triang.(PS4), Y (Xbox), X (Switch) + ImGuiNavInput_PadMenu, // tap: toggle menu / hold: focus, move, resize // e.g. Square (PS4), X (Xbox), Y (Switch) + ImGuiNavInput_PadDpadLeft, // move / tweak / resize window (w/ PadMenu) // e.g. D-pad Left/Right/Up/Down + ImGuiNavInput_PadDpadRight, // + ImGuiNavInput_PadDpadUp, // + ImGuiNavInput_PadDpadDown, // + ImGuiNavInput_PadLStickLeft, // scroll / move window (w/ PadMenu) // e.g. Left Analog Stick Left/Right/Up/Down + ImGuiNavInput_PadLStickRight, // + ImGuiNavInput_PadLStickUp, // + ImGuiNavInput_PadLStickDown, // + ImGuiNavInput_PadFocusPrev, // next window (w/ PadMenu) // e.g. L1 (PS4), LB (Xbox), L (Switch) + ImGuiNavInput_PadFocusNext, // prev window (w/ PadMenu) // e.g. R1 (PS4), RB (Xbox), R (Switch) + ImGuiNavInput_PadTweakSlow, // slower tweaks // e.g. L2 (PS4), LT (Xbox), ZL (Switch), Analog + ImGuiNavInput_PadTweakFast, // faster tweaks // e.g. R2 (PS4), RT (Xbox), ZR (Switch), Analog // Keyboard Mapping // [BETA] To use keyboard control you currently need to map keys to those gamepad inputs: PadActivate (Enter), PadCancel (Escape), PadInput (Enter). // Will add specialized keyboard mappings as we add features and clarify the input interface. - ImGuiNavInput_KeyMenu, // toggle menu // e.g. ALT - ImGuiNavInput_KeyLeft, // move left // e.g. Arrow keys + ImGuiNavInput_KeyMenu, // toggle menu // e.g. Alt + ImGuiNavInput_KeyLeft, // move left // e.g. Arrow keys ImGuiNavInput_KeyRight, // move right ImGuiNavInput_KeyUp, // move up ImGuiNavInput_KeyDown, // move down From 7e32fc7109995825cf7801d3b36547d5092a1c44 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Feb 2018 19:23:12 +0100 Subject: [PATCH 314/319] Nav: Toward automatically mapping keyboard input. Renamed ImGuiNavInput_PadXXX to ImGuiNavInput_XXX. Renamed ImGuiNavInput_KeyXXX to ImGuiNavInput_KeyXXX_ (internal). (#787) --- .../opengl3_example/imgui_impl_glfw_gl3.cpp | 32 +++++++------- imgui.cpp | 42 +++++++++---------- imgui.h | 42 +++++++++---------- imgui_internal.h | 8 ++-- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp index dcd40772..7444b10b 100644 --- a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp +++ b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp @@ -444,22 +444,22 @@ void ImGui_ImplGlfwGL3_NewFrame() int axes_count = 0, buttons_count = 0; const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); - MAP_BUTTON(ImGuiNavInput_PadActivate, 0); // Cross / A - MAP_BUTTON(ImGuiNavInput_PadCancel, 1); // Circle / B - MAP_BUTTON(ImGuiNavInput_PadMenu, 2); // Square / X - MAP_BUTTON(ImGuiNavInput_PadInput, 3); // Triangle / Y - MAP_BUTTON(ImGuiNavInput_PadDpadLeft, 13); // D-Pad Left - MAP_BUTTON(ImGuiNavInput_PadDpadRight, 11); // D-Pad Right - MAP_BUTTON(ImGuiNavInput_PadDpadUp, 10); // D-Pad Up - MAP_BUTTON(ImGuiNavInput_PadDpadDown, 12); // D-Pad Down - MAP_BUTTON(ImGuiNavInput_PadFocusPrev, 4); // L Trigger - MAP_BUTTON(ImGuiNavInput_PadFocusNext, 5); // R Trigger - MAP_BUTTON(ImGuiNavInput_PadTweakSlow, 4); // L Trigger - MAP_BUTTON(ImGuiNavInput_PadTweakFast, 5); // R Trigger - MAP_ANALOG(ImGuiNavInput_PadLStickLeft, 0, -0.3f, -0.9f); - MAP_ANALOG(ImGuiNavInput_PadLStickRight,0, +0.3f, +0.9f); - MAP_ANALOG(ImGuiNavInput_PadLStickUp, 1, +0.3f, +0.9f); - MAP_ANALOG(ImGuiNavInput_PadLStickDown, 1, -0.3f, -0.9f); + MAP_BUTTON(ImGuiNavInput_Activate, 0); // Cross / A + MAP_BUTTON(ImGuiNavInput_Cancel, 1); // Circle / B + MAP_BUTTON(ImGuiNavInput_Menu, 2); // Square / X + MAP_BUTTON(ImGuiNavInput_Input, 3); // Triangle / Y + MAP_BUTTON(ImGuiNavInput_DpadLeft, 13); // D-Pad Left + MAP_BUTTON(ImGuiNavInput_DpadRight, 11); // D-Pad Right + MAP_BUTTON(ImGuiNavInput_DpadUp, 10); // D-Pad Up + MAP_BUTTON(ImGuiNavInput_DpadDown, 12); // D-Pad Down + MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L Trigger + MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R Trigger + MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L Trigger + MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R Trigger + MAP_ANALOG(ImGuiNavInput_LStickLeft, 0, -0.3f, -0.9f); + MAP_ANALOG(ImGuiNavInput_LStickRight,0, +0.3f, +0.9f); + MAP_ANALOG(ImGuiNavInput_LStickUp, 1, +0.3f, +0.9f); + MAP_ANALOG(ImGuiNavInput_LStickDown, 1, -0.3f, -0.9f); #undef MAP_BUTTON #undef MAP_ANALOG } diff --git a/imgui.cpp b/imgui.cpp index 6d485418..14dc9d65 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2739,14 +2739,14 @@ ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInput { ImVec2 delta(0.0f, 0.0f); if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft, mode), GetNavInputAmount(ImGuiNavInput_KeyDown, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp, mode)); + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_PadDpadRight, mode) - GetNavInputAmount(ImGuiNavInput_PadDpadLeft, mode), GetNavInputAmount(ImGuiNavInput_PadDpadDown, mode) - GetNavInputAmount(ImGuiNavInput_PadDpadUp, mode)); + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode)); if (dir_sources & ImGuiNavDirSourceFlags_PadLStick) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_PadLStickRight, mode) - GetNavInputAmount(ImGuiNavInput_PadLStickLeft, mode), GetNavInputAmount(ImGuiNavInput_PadLStickDown, mode) - GetNavInputAmount(ImGuiNavInput_PadLStickUp, mode)); - if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_PadTweakSlow)) + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode)); + if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow)) delta *= slow_factor; - if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_PadTweakFast)) + if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast)) delta *= fast_factor; return delta; } @@ -2773,7 +2773,7 @@ static void ImGui::NavUpdateWindowing() ImGuiWindow* apply_focus_window = NULL; bool apply_toggle_layer = false; - bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_PadMenu, ImGuiInputReadMode_Pressed); + bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard); if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavigable(g.Windows.Size - 1, -INT_MAX, -1)) @@ -2792,7 +2792,7 @@ static void ImGui::NavUpdateWindowing() g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.20f) / 0.05f)); // Select window to focus - const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_PadFocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_PadFocusNext, ImGuiInputReadMode_RepeatSlow); + const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow); if (focus_change_dir != 0) { NavUpdateWindowingHighlightWindow(focus_change_dir); @@ -2800,7 +2800,7 @@ static void ImGui::NavUpdateWindowing() } // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most) - if (!IsNavInputDown(ImGuiNavInput_PadMenu)) + if (!IsNavInputDown(ImGuiNavInput_Menu)) { g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore. if (g.NavWindowingToggleLayer && g.NavWindow) @@ -2824,7 +2824,7 @@ static void ImGui::NavUpdateWindowing() // Keyboard: Press and Release ALT to toggle menu layer // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB - if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu, ImGuiInputReadMode_Released)) + if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) apply_toggle_layer = true; @@ -3003,7 +3003,7 @@ static void ImGui::NavUpdate() g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; // Process NavCancel input (to close a popup, get back to parent, clear focus) - if (IsNavInputPressed(ImGuiNavInput_PadCancel, ImGuiInputReadMode_Pressed)) + if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) { if (g.ActiveId != 0) { @@ -3045,15 +3045,15 @@ static void ImGui::NavUpdate() g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { - bool activate_down = IsNavInputDown(ImGuiNavInput_PadActivate); - bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_PadActivate, ImGuiInputReadMode_Pressed); + bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); + bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); if (g.ActiveId == 0 && activate_pressed) g.NavActivateId = g.NavId; if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) g.NavActivateDownId = g.NavId; if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) g.NavActivatePressedId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_PadInput, ImGuiInputReadMode_Pressed)) + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) g.NavInputId = g.NavId; } if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) @@ -3074,10 +3074,10 @@ static void ImGui::NavUpdate() g.NavMoveDir = ImGuiDir_None; if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { - if ((allowed_dir_flags & (1<= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits diff --git a/imgui.h b/imgui.h index 136f296e..2efd0d90 100644 --- a/imgui.h +++ b/imgui.h @@ -710,30 +710,30 @@ enum ImGuiKey_ enum ImGuiNavInput_ { // Gamepad Mapping - ImGuiNavInput_PadActivate, // activate / open / toggle / tweak value // e.g. Circle (PS4), A (Xbox), B (Switch) - ImGuiNavInput_PadCancel, // cancel / close / exit // e.g. Cross (PS4), B (Xbox), A (Switch) - ImGuiNavInput_PadInput, // text input / on-screen keyboard // e.g. Triang.(PS4), Y (Xbox), X (Switch) - ImGuiNavInput_PadMenu, // tap: toggle menu / hold: focus, move, resize // e.g. Square (PS4), X (Xbox), Y (Switch) - ImGuiNavInput_PadDpadLeft, // move / tweak / resize window (w/ PadMenu) // e.g. D-pad Left/Right/Up/Down - ImGuiNavInput_PadDpadRight, // - ImGuiNavInput_PadDpadUp, // - ImGuiNavInput_PadDpadDown, // - ImGuiNavInput_PadLStickLeft, // scroll / move window (w/ PadMenu) // e.g. Left Analog Stick Left/Right/Up/Down - ImGuiNavInput_PadLStickRight, // - ImGuiNavInput_PadLStickUp, // - ImGuiNavInput_PadLStickDown, // - ImGuiNavInput_PadFocusPrev, // next window (w/ PadMenu) // e.g. L1 (PS4), LB (Xbox), L (Switch) - ImGuiNavInput_PadFocusNext, // prev window (w/ PadMenu) // e.g. R1 (PS4), RB (Xbox), R (Switch) - ImGuiNavInput_PadTweakSlow, // slower tweaks // e.g. L2 (PS4), LT (Xbox), ZL (Switch), Analog - ImGuiNavInput_PadTweakFast, // faster tweaks // e.g. R2 (PS4), RT (Xbox), ZR (Switch), Analog + ImGuiNavInput_Activate, // activate / open / toggle / tweak value // e.g. Circle (PS4), A (Xbox), B (Switch) + ImGuiNavInput_Cancel, // cancel / close / exit // e.g. Cross (PS4), B (Xbox), A (Switch) + ImGuiNavInput_Input, // text input / on-screen keyboard // e.g. Triang.(PS4), Y (Xbox), X (Switch) + ImGuiNavInput_Menu, // tap: toggle menu / hold: focus, move, resize // e.g. Square (PS4), X (Xbox), Y (Switch) + ImGuiNavInput_DpadLeft, // move / tweak / resize window (w/ PadMenu) // e.g. D-pad Left/Right/Up/Down + ImGuiNavInput_DpadRight, // + ImGuiNavInput_DpadUp, // + ImGuiNavInput_DpadDown, // + ImGuiNavInput_LStickLeft, // scroll / move window (w/ PadMenu) // e.g. Left Analog Stick Left/Right/Up/Down + ImGuiNavInput_LStickRight, // + ImGuiNavInput_LStickUp, // + ImGuiNavInput_LStickDown, // + ImGuiNavInput_FocusPrev, // next window (w/ PadMenu) // e.g. L1 (PS4), LB (Xbox), L (Switch) + ImGuiNavInput_FocusNext, // prev window (w/ PadMenu) // e.g. R1 (PS4), RB (Xbox), R (Switch) + ImGuiNavInput_TweakSlow, // slower tweaks // e.g. L2 (PS4), LT (Xbox), ZL (Switch), Analog + ImGuiNavInput_TweakFast, // faster tweaks // e.g. R2 (PS4), RT (Xbox), ZR (Switch), Analog // Keyboard Mapping // [BETA] To use keyboard control you currently need to map keys to those gamepad inputs: PadActivate (Enter), PadCancel (Escape), PadInput (Enter). // Will add specialized keyboard mappings as we add features and clarify the input interface. - ImGuiNavInput_KeyMenu, // toggle menu // e.g. Alt - ImGuiNavInput_KeyLeft, // move left // e.g. Arrow keys - ImGuiNavInput_KeyRight, // move right - ImGuiNavInput_KeyUp, // move up - ImGuiNavInput_KeyDown, // move down + ImGuiNavInput_KeyMenu_, // toggle menu // e.g. Alt + ImGuiNavInput_KeyLeft_, // move left // e.g. Arrow keys + ImGuiNavInput_KeyRight_, // move right + ImGuiNavInput_KeyUp_, // move up + ImGuiNavInput_KeyDown_, // move down ImGuiNavInput_COUNT, }; diff --git a/imgui_internal.h b/imgui_internal.h index bbd9e48b..29c2d588 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -616,10 +616,10 @@ struct ImGuiContext // Navigation data (for gamepad/keyboard) ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' ImGuiID NavId; // Focused item for navigation - ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && IsNavInputPressed(ImGuiNavInput_PadActivate) ? NavId : 0, also set when calling ActivateItem() - ImGuiID NavActivateDownId; // ~~ IsNavInputDown(ImGuiNavInput_PadActivate) ? NavId : 0 - ImGuiID NavActivatePressedId; // ~~ IsNavInputPressed(ImGuiNavInput_PadActivate) ? NavId : 0 - ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_PadInput) ? NavId : 0 + ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0, also set when calling ActivateItem() + ImGuiID NavActivateDownId; // ~~ IsNavInputDown(ImGuiNavInput_Activate) ? NavId : 0 + ImGuiID NavActivatePressedId; // ~~ IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0 + ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0 ImGuiID NavJustTabbedId; // Just tabbed to this id. ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest) From 057807f4a763d11477e0cc70acaf63b85b46bf77 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Feb 2018 19:29:31 +0100 Subject: [PATCH 315/319] Added ImGuiKey_Space, mapped in every examples. Will be required for navigation. (#787) --- examples/allegro5_example/imgui_impl_a5.cpp | 1 + .../imguiex-ios/imgui_impl_ios.mm | 1 + .../directx10_example/imgui_impl_dx10.cpp | 1 + .../directx11_example/imgui_impl_dx11.cpp | 1 + examples/directx9_example/imgui_impl_dx9.cpp | 1 + .../imgui_impl_marmalade.cpp | 1 + .../opengl2_example/imgui_impl_glfw_gl2.cpp | 1 + .../opengl3_example/imgui_impl_glfw_gl3.cpp | 1 + .../imgui_impl_sdl_gl2.cpp | 1 + .../imgui_impl_sdl_gl3.cpp | 1 + .../vulkan_example/imgui_impl_glfw_vulkan.cpp | 1 + imgui.h | 23 ++++++++++--------- 12 files changed, 23 insertions(+), 11 deletions(-) diff --git a/examples/allegro5_example/imgui_impl_a5.cpp b/examples/allegro5_example/imgui_impl_a5.cpp index 1eb118e6..81062f22 100644 --- a/examples/allegro5_example/imgui_impl_a5.cpp +++ b/examples/allegro5_example/imgui_impl_a5.cpp @@ -180,6 +180,7 @@ bool ImGui_ImplA5_Init(ALLEGRO_DISPLAY* display) io.KeyMap[ImGuiKey_Insert] = ALLEGRO_KEY_INSERT; io.KeyMap[ImGuiKey_Delete] = ALLEGRO_KEY_DELETE; io.KeyMap[ImGuiKey_Backspace] = ALLEGRO_KEY_BACKSPACE; + io.KeyMap[ImGuiKey_Space] = ALLEGRO_KEY_SPACE; io.KeyMap[ImGuiKey_Enter] = ALLEGRO_KEY_ENTER; io.KeyMap[ImGuiKey_Escape] = ALLEGRO_KEY_ESCAPE; io.KeyMap[ImGuiKey_A] = ALLEGRO_KEY_A; diff --git a/examples/apple_example/imguiex-ios/imgui_impl_ios.mm b/examples/apple_example/imguiex-ios/imgui_impl_ios.mm index 20b5b733..440b586a 100644 --- a/examples/apple_example/imguiex-ios/imgui_impl_ios.mm +++ b/examples/apple_example/imguiex-ios/imgui_impl_ios.mm @@ -491,6 +491,7 @@ void ImGui_ClipboardCallback(uSynergyCookie cookie, enum uSynergyClipboardFormat io.KeyMap[ImGuiKey_Insert] = kVK_Help+1; io.KeyMap[ImGuiKey_Delete] = kVK_ForwardDelete+1; io.KeyMap[ImGuiKey_Backspace] = kVK_Delete+1; + io.KeyMap[ImGuiKey_Space] = kVK_Space+1; io.KeyMap[ImGuiKey_Enter] = kVK_Return+1; io.KeyMap[ImGuiKey_Escape] = kVK_Escape+1; io.KeyMap[ImGuiKey_A] = kVK_ANSI_A+1; diff --git a/examples/directx10_example/imgui_impl_dx10.cpp b/examples/directx10_example/imgui_impl_dx10.cpp index e3080f9c..7a788090 100644 --- a/examples/directx10_example/imgui_impl_dx10.cpp +++ b/examples/directx10_example/imgui_impl_dx10.cpp @@ -553,6 +553,7 @@ bool ImGui_ImplDX10_Init(void* hwnd, ID3D10Device* device) io.KeyMap[ImGuiKey_Insert] = VK_INSERT; io.KeyMap[ImGuiKey_Delete] = VK_DELETE; io.KeyMap[ImGuiKey_Backspace] = VK_BACK; + io.KeyMap[ImGuiKey_Space] = VK_SPACE; io.KeyMap[ImGuiKey_Enter] = VK_RETURN; io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; io.KeyMap[ImGuiKey_A] = 'A'; diff --git a/examples/directx11_example/imgui_impl_dx11.cpp b/examples/directx11_example/imgui_impl_dx11.cpp index fbcfd953..5b7cd97d 100644 --- a/examples/directx11_example/imgui_impl_dx11.cpp +++ b/examples/directx11_example/imgui_impl_dx11.cpp @@ -556,6 +556,7 @@ bool ImGui_ImplDX11_Init(void* hwnd, ID3D11Device* device, ID3D11DeviceContex io.KeyMap[ImGuiKey_Insert] = VK_INSERT; io.KeyMap[ImGuiKey_Delete] = VK_DELETE; io.KeyMap[ImGuiKey_Backspace] = VK_BACK; + io.KeyMap[ImGuiKey_Space] = VK_SPACE; io.KeyMap[ImGuiKey_Enter] = VK_RETURN; io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; io.KeyMap[ImGuiKey_A] = 'A'; diff --git a/examples/directx9_example/imgui_impl_dx9.cpp b/examples/directx9_example/imgui_impl_dx9.cpp index 8e0808fd..e54756d9 100644 --- a/examples/directx9_example/imgui_impl_dx9.cpp +++ b/examples/directx9_example/imgui_impl_dx9.cpp @@ -272,6 +272,7 @@ bool ImGui_ImplDX9_Init(void* hwnd, IDirect3DDevice9* device) io.KeyMap[ImGuiKey_Insert] = VK_INSERT; io.KeyMap[ImGuiKey_Delete] = VK_DELETE; io.KeyMap[ImGuiKey_Backspace] = VK_BACK; + io.KeyMap[ImGuiKey_Space] = VK_SPACE; io.KeyMap[ImGuiKey_Enter] = VK_RETURN; io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; io.KeyMap[ImGuiKey_A] = 'A'; diff --git a/examples/marmalade_example/imgui_impl_marmalade.cpp b/examples/marmalade_example/imgui_impl_marmalade.cpp index e8d60af9..2ba632fa 100644 --- a/examples/marmalade_example/imgui_impl_marmalade.cpp +++ b/examples/marmalade_example/imgui_impl_marmalade.cpp @@ -224,6 +224,7 @@ bool ImGui_Marmalade_Init(bool install_callbacks) io.KeyMap[ImGuiKey_Insert] = s3eKeyInsert; io.KeyMap[ImGuiKey_Delete] = s3eKeyDelete; io.KeyMap[ImGuiKey_Backspace] = s3eKeyBackspace; + io.KeyMap[ImGuiKey_Space] = s3eKeySpace; io.KeyMap[ImGuiKey_Enter] = s3eKeyEnter; io.KeyMap[ImGuiKey_Escape] = s3eKeyEsc; io.KeyMap[ImGuiKey_A] = s3eKeyA; diff --git a/examples/opengl2_example/imgui_impl_glfw_gl2.cpp b/examples/opengl2_example/imgui_impl_glfw_gl2.cpp index 954fbcde..5ac32e94 100644 --- a/examples/opengl2_example/imgui_impl_glfw_gl2.cpp +++ b/examples/opengl2_example/imgui_impl_glfw_gl2.cpp @@ -217,6 +217,7 @@ bool ImGui_ImplGlfwGL2_Init(GLFWwindow* window, bool install_callbacks) io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT; io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; + io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; diff --git a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp index 7444b10b..f0d2731e 100644 --- a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp +++ b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp @@ -331,6 +331,7 @@ bool ImGui_ImplGlfwGL3_Init(GLFWwindow* window, bool install_callbacks) io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT; io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; + io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; diff --git a/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp b/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp index 56776310..f8fd027b 100644 --- a/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp +++ b/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp @@ -220,6 +220,7 @@ bool ImGui_ImplSdlGL2_Init(SDL_Window* window) io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT; io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE; io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE; + io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE; io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN; io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE; io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A; diff --git a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp index 0619b6ad..5d42852b 100644 --- a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp +++ b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp @@ -332,6 +332,7 @@ bool ImGui_ImplSdlGL3_Init(SDL_Window* window) io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT; io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE; io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE; + io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE; io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN; io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE; io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A; diff --git a/examples/vulkan_example/imgui_impl_glfw_vulkan.cpp b/examples/vulkan_example/imgui_impl_glfw_vulkan.cpp index 3edf2097..bd5eca0e 100644 --- a/examples/vulkan_example/imgui_impl_glfw_vulkan.cpp +++ b/examples/vulkan_example/imgui_impl_glfw_vulkan.cpp @@ -755,6 +755,7 @@ bool ImGui_ImplGlfwVulkan_Init(GLFWwindow* window, bool install_callbacks, Im io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT; io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; + io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; diff --git a/imgui.h b/imgui.h index 2efd0d90..01a5ce50 100644 --- a/imgui.h +++ b/imgui.h @@ -693,6 +693,7 @@ enum ImGuiKey_ ImGuiKey_Backspace, // for text edit ImGuiKey_Enter, // for text edit ImGuiKey_Escape, // for text edit + ImGuiKey_Space, ImGuiKey_A, // for text edit CTRL+A: select all ImGuiKey_C, // for text edit CTRL+C: copy ImGuiKey_V, // for text edit CTRL+V: paste @@ -999,17 +1000,17 @@ struct ImGuiIO // Input - Fill before calling NewFrame() //------------------------------------------------------------------ - ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX,-FLT_MAX) if mouse is unavailable (on another screen, etc.) - bool MouseDown[5]; // Mouse buttons: left, right, middle + extras. ImGui itself mostly only uses left button (BeginPopupContext** are using right button). Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. - float MouseWheel; // Mouse wheel: 1 unit scrolls about 5 lines text. - float MouseWheelH; // Mouse wheel (Horizontal). Most users don't have a mouse with an horizontal wheel, may not be filled by all back ends. - bool MouseDrawCursor; // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). - bool KeyCtrl; // Keyboard modifier pressed: Control - bool KeyShift; // Keyboard modifier pressed: Shift - bool KeyAlt; // Keyboard modifier pressed: Alt - bool KeySuper; // Keyboard modifier pressed: Cmd/Super/Windows - bool KeysDown[512]; // Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). - ImWchar InputCharacters[16+1]; // List of characters input (translated by user from keypress+keyboard state). Fill using AddInputCharacter() helper. + ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX,-FLT_MAX) if mouse is unavailable (on another screen, etc.) + bool MouseDown[5]; // Mouse buttons: left, right, middle + extras. ImGui itself mostly only uses left button (BeginPopupContext** are using right button). Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. + float MouseWheel; // Mouse wheel: 1 unit scrolls about 5 lines text. + float MouseWheelH; // Mouse wheel (Horizontal). Most users don't have a mouse with an horizontal wheel, may not be filled by all back ends. + bool MouseDrawCursor; // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). + bool KeyCtrl; // Keyboard modifier pressed: Control + bool KeyShift; // Keyboard modifier pressed: Shift + bool KeyAlt; // Keyboard modifier pressed: Alt + bool KeySuper; // Keyboard modifier pressed: Cmd/Super/Windows + bool KeysDown[512]; // Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). + ImWchar InputCharacters[16+1]; // List of characters input (translated by user from keypress+keyboard state). Fill using AddInputCharacter() helper. float NavInputs[ImGuiNavInput_COUNT]; // Functions From 9e3a807813eaf4e65475f0a023ba00f5f174decf Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Feb 2018 19:34:09 +0100 Subject: [PATCH 316/319] Removed comments --- imgui.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/imgui.h b/imgui.h index 01a5ce50..397756ed 100644 --- a/imgui.h +++ b/imgui.h @@ -679,21 +679,21 @@ enum ImGuiDragDropFlags_ // User fill ImGuiIO.KeyMap[] array with indices into the ImGuiIO.KeysDown[512] array enum ImGuiKey_ { - ImGuiKey_Tab, // for tabbing through fields - ImGuiKey_LeftArrow, // for text edit - ImGuiKey_RightArrow,// for text edit - ImGuiKey_UpArrow, // for text edit - ImGuiKey_DownArrow, // for text edit + ImGuiKey_Tab, + ImGuiKey_LeftArrow, + ImGuiKey_RightArrow, + ImGuiKey_UpArrow, + ImGuiKey_DownArrow, ImGuiKey_PageUp, ImGuiKey_PageDown, - ImGuiKey_Home, // for text edit - ImGuiKey_End, // for text edit - ImGuiKey_Insert, // for text edit - ImGuiKey_Delete, // for text edit - ImGuiKey_Backspace, // for text edit - ImGuiKey_Enter, // for text edit - ImGuiKey_Escape, // for text edit + ImGuiKey_Home, + ImGuiKey_End, + ImGuiKey_Insert, + ImGuiKey_Delete, + ImGuiKey_Backspace, ImGuiKey_Space, + ImGuiKey_Enter, + ImGuiKey_Escape, ImGuiKey_A, // for text edit CTRL+A: select all ImGuiKey_C, // for text edit CTRL+C: copy ImGuiKey_V, // for text edit CTRL+V: paste From 3171f90a1a31e0472d951cddc4eb8fc9b96e6421 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Feb 2018 19:54:30 +0100 Subject: [PATCH 317/319] Nav: Keyboard is now automatically mapped based on io.KeyDown[]. (#787) --- .../directx11_example/imgui_impl_dx11.cpp | 21 -------- examples/directx11_example/imgui_impl_dx11.h | 1 - .../opengl3_example/imgui_impl_glfw_gl3.cpp | 30 ++---------- .../opengl3_example/imgui_impl_glfw_gl3.h | 1 - .../imgui_impl_sdl_gl3.cpp | 24 ---------- .../sdl_opengl3_example/imgui_impl_sdl_gl3.h | 1 - imgui.cpp | 32 +++++++++++-- imgui.h | 48 ++++++++++--------- 8 files changed, 59 insertions(+), 99 deletions(-) diff --git a/examples/directx11_example/imgui_impl_dx11.cpp b/examples/directx11_example/imgui_impl_dx11.cpp index 5b7cd97d..9b8c80c4 100644 --- a/examples/directx11_example/imgui_impl_dx11.cpp +++ b/examples/directx11_example/imgui_impl_dx11.cpp @@ -2,7 +2,6 @@ // Implemented features: // [X] User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Keyboard navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableKeyboard'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). @@ -609,26 +608,6 @@ void ImGui_ImplDX11_NewFrame() // io.MouseDown : filled by WM_*BUTTON* events // io.MouseWheel : filled by WM_MOUSEWHEEL events - // Gamepad/keyboard navigation mapping [BETA] - memset(io.NavInputs, 0, sizeof(io.NavInputs)); - if (io.NavFlags & ImGuiNavFlags_EnableKeyboard) - { - // Update keyboard - // FIXME-NAV: We are still using some of the ImGuiNavInput_PadXXX enums as keyboard support is incomplete. - #define MAP_KEY(NAV_NO, KEY_NO) { if (io.KeysDown[KEY_NO]) io.NavInputs[NAV_NO] = 1.0f; } - MAP_KEY(ImGuiNavInput_KeyLeft, VK_LEFT); - MAP_KEY(ImGuiNavInput_KeyRight, VK_RIGHT); - MAP_KEY(ImGuiNavInput_KeyUp, VK_UP); - MAP_KEY(ImGuiNavInput_KeyDown, VK_DOWN); - MAP_KEY(ImGuiNavInput_KeyMenu, VK_MENU); - MAP_KEY(ImGuiNavInput_PadActivate, VK_SPACE); - MAP_KEY(ImGuiNavInput_PadCancel, VK_ESCAPE); - MAP_KEY(ImGuiNavInput_PadInput, VK_RETURN); - MAP_KEY(ImGuiNavInput_PadTweakFast, VK_SHIFT); - MAP_KEY(ImGuiNavInput_PadTweakSlow, VK_CONTROL); - #undef MAP_KEY - } - // Set OS mouse position if requested last frame by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) if (io.WantMoveMouse) { diff --git a/examples/directx11_example/imgui_impl_dx11.h b/examples/directx11_example/imgui_impl_dx11.h index fc2b6bdb..90bfe4fe 100644 --- a/examples/directx11_example/imgui_impl_dx11.h +++ b/examples/directx11_example/imgui_impl_dx11.h @@ -2,7 +2,6 @@ // Implemented features: // [X] User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Keyboard navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableKeyboard'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp index f0d2731e..86d94b26 100644 --- a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp +++ b/examples/opengl3_example/imgui_impl_glfw_gl3.cpp @@ -4,7 +4,6 @@ // Implemented features: // [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Keyboard navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableKeyboard'. // [X] Gamepad navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableGamepad'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. @@ -416,27 +415,8 @@ void ImGui_ImplGlfwGL3_NewFrame() // Hide OS mouse cursor if ImGui is drawing it glfwSetInputMode(g_Window, GLFW_CURSOR, io.MouseDrawCursor ? GLFW_CURSOR_HIDDEN : GLFW_CURSOR_NORMAL); - // Gamepad/keyboard navigation mapping [BETA] + // Gamepad navigation mapping [BETA] memset(io.NavInputs, 0, sizeof(io.NavInputs)); - if (io.NavFlags & ImGuiNavFlags_EnableKeyboard) - { - // Update keyboard - // FIXME-NAV: We are still using some of the ImGuiNavInput_PadXXX enums as keyboard support is incomplete. - #define MAP_KEY(NAV_NO, KEY_NO) { if (io.KeysDown[KEY_NO]) io.NavInputs[NAV_NO] = 1.0f; } - MAP_KEY(ImGuiNavInput_KeyLeft, GLFW_KEY_LEFT); - MAP_KEY(ImGuiNavInput_KeyRight, GLFW_KEY_RIGHT); - MAP_KEY(ImGuiNavInput_KeyUp, GLFW_KEY_UP); - MAP_KEY(ImGuiNavInput_KeyDown, GLFW_KEY_DOWN); - MAP_KEY(ImGuiNavInput_KeyMenu, GLFW_KEY_LEFT_ALT); - MAP_KEY(ImGuiNavInput_PadActivate, GLFW_KEY_SPACE); - MAP_KEY(ImGuiNavInput_PadCancel, GLFW_KEY_ESCAPE); - MAP_KEY(ImGuiNavInput_PadInput, GLFW_KEY_ENTER); - MAP_KEY(ImGuiNavInput_PadTweakSlow, GLFW_KEY_LEFT_ALT); - MAP_KEY(ImGuiNavInput_PadTweakSlow, GLFW_KEY_RIGHT_ALT); - MAP_KEY(ImGuiNavInput_PadTweakFast, GLFW_KEY_LEFT_SHIFT); - MAP_KEY(ImGuiNavInput_PadTweakFast, GLFW_KEY_RIGHT_SHIFT); - #undef MAP_KEY - } if (io.NavFlags & ImGuiNavFlags_EnableGamepad) { // Update gamepad inputs @@ -453,10 +433,10 @@ void ImGui_ImplGlfwGL3_NewFrame() MAP_BUTTON(ImGuiNavInput_DpadRight, 11); // D-Pad Right MAP_BUTTON(ImGuiNavInput_DpadUp, 10); // D-Pad Up MAP_BUTTON(ImGuiNavInput_DpadDown, 12); // D-Pad Down - MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L Trigger - MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R Trigger - MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L Trigger - MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R Trigger + MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L1 / LB + MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R1 / RB + MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L1 / LB + MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R1 / RB MAP_ANALOG(ImGuiNavInput_LStickLeft, 0, -0.3f, -0.9f); MAP_ANALOG(ImGuiNavInput_LStickRight,0, +0.3f, +0.9f); MAP_ANALOG(ImGuiNavInput_LStickUp, 1, +0.3f, +0.9f); diff --git a/examples/opengl3_example/imgui_impl_glfw_gl3.h b/examples/opengl3_example/imgui_impl_glfw_gl3.h index e7a4c2f6..6b4101f2 100644 --- a/examples/opengl3_example/imgui_impl_glfw_gl3.h +++ b/examples/opengl3_example/imgui_impl_glfw_gl3.h @@ -4,7 +4,6 @@ // Implemented features: // [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Keyboard navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableKeyboard'. // [X] Gamepad navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableGamepad'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. diff --git a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp index 5d42852b..b1c39ae6 100644 --- a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp +++ b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp @@ -4,7 +4,6 @@ // Implemented features: // [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Keyboard navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableKeyboard'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). @@ -414,29 +413,6 @@ void ImGui_ImplSdlGL3_NewFrame(SDL_Window* window) // Hide OS mouse cursor if ImGui is drawing it SDL_ShowCursor(io.MouseDrawCursor ? 0 : 1); - // Gamepad/keyboard navigation mapping [BETA] - memset(io.NavInputs, 0, sizeof(io.NavInputs)); - if (io.NavFlags & ImGuiNavFlags_EnableKeyboard) - { - // Update keyboard - // FIXME-NAV: We are still using some of the ImGuiNavInput_PadXXX enums as keyboard support is incomplete. - #define MAP_KEY(NAV_NO, KEY_NO) { if (io.KeysDown[KEY_NO]) io.NavInputs[NAV_NO] = 1.0f; } - MAP_KEY(ImGuiNavInput_KeyLeft, SDL_SCANCODE_LEFT); - MAP_KEY(ImGuiNavInput_KeyRight, SDL_SCANCODE_RIGHT); - MAP_KEY(ImGuiNavInput_KeyUp, SDL_SCANCODE_UP); - MAP_KEY(ImGuiNavInput_KeyDown, SDL_SCANCODE_DOWN); - MAP_KEY(ImGuiNavInput_KeyMenu, SDL_SCANCODE_LALT); - MAP_KEY(ImGuiNavInput_KeyMenu, SDL_SCANCODE_RALT); - MAP_KEY(ImGuiNavInput_PadActivate, SDL_SCANCODE_SPACE); - MAP_KEY(ImGuiNavInput_PadCancel, SDL_SCANCODE_ESCAPE); - MAP_KEY(ImGuiNavInput_PadInput, SDL_SCANCODE_RETURN); - MAP_KEY(ImGuiNavInput_PadTweakSlow, SDL_SCANCODE_LALT); - MAP_KEY(ImGuiNavInput_PadTweakSlow, SDL_SCANCODE_LALT); - MAP_KEY(ImGuiNavInput_PadTweakFast, SDL_SCANCODE_LSHIFT); - MAP_KEY(ImGuiNavInput_PadTweakFast, SDL_SCANCODE_RSHIFT); - #undef MAP_KEY - } - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. ImGui::NewFrame(); } diff --git a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h index fc7e1e96..3f76e294 100644 --- a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h +++ b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h @@ -4,7 +4,6 @@ // Implemented features: // [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Keyboard navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableKeyboard'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/imgui.cpp b/imgui.cpp index 14dc9d65..3895c6c9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2927,6 +2927,28 @@ static void ImGui::NavUpdate() if (g.NavScoringCount > 0) printf("[%05d] NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); #endif + // Update Keyboard->Nav inputs mapping + memset(g.IO.NavInputs + ImGuiNavInput_InternalStart_, 0, (ImGuiNavInput_COUNT - ImGuiNavInput_InternalStart_) * sizeof(g.IO.NavInputs[0])); + if (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard) + { + #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (g.IO.KeyMap[_KEY] != -1 && IsKeyDown(g.IO.KeyMap[_KEY])) g.IO.NavInputs[_NAV_INPUT] = 1.0f; + NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); + NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input ); + NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel ); + NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ ); + NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_); + NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ ); + NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ ); + if (g.IO.KeyCtrl) g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; + if (g.IO.KeyShift) g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; + if (g.IO.KeyAlt) g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; +#undef NAV_MAP_KEY + } + + memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); + for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) + g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f; + // Process navigation init request (select first/default focus) if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove)) { @@ -3182,6 +3204,10 @@ void ImGui::NewFrame() for (int n = 0; n < ImGuiKey_COUNT; n++) IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)"); + // Do a simple check for required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was super recently added in 1.54 WIP) + if (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard) + IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); + // Initialize on first frame if (!g.Initialized) Initialize(); @@ -3235,11 +3261,8 @@ void ImGui::NewFrame() memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f; - memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); - for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) - g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f; - // Update directional navigation which may override MousePos if ImGuiNavFlags_MoveMouse is enabled. + // Update gamepad/keyboard directional navigation NavUpdate(); // Update mouse input state @@ -3952,6 +3975,7 @@ void ImGui::EndFrame() // Clear Input data for next frame g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f; memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); + memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs)); g.FrameCountEnded = g.FrameCount; } diff --git a/imgui.h b/imgui.h index 397756ed..c2163640 100644 --- a/imgui.h +++ b/imgui.h @@ -704,45 +704,49 @@ enum ImGuiKey_ }; // [BETA] Gamepad/Keyboard directional navigation -// Fill ImGuiIO.NavInputs[] float array every frame to feed gamepad/keyboard navigation inputs. -// 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. -// ImGui uses a simple >0.0f for activation testing, and won't attempt to test for a dead-zone. -// Your code passing analog gamepad values is likely to want to transform your raw inputs, using a dead-zone and maybe a power curve. +// Keyboard: +// - io.NavInputs[] is automatically filled in by NewFrame() if you set io.NavFlags |= ImGuiNavFlags_EnableKeyboard. +// Gamepad: +// - Fill io.NavInputs[] every frame with your gamepad inputs. Note that io.NavInputs[] is _cleared_ in EndFrame(). +// 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. +// - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. +// Your code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, maybe a power curve, etc.). enum ImGuiNavInput_ { // Gamepad Mapping - ImGuiNavInput_Activate, // activate / open / toggle / tweak value // e.g. Circle (PS4), A (Xbox), B (Switch) - ImGuiNavInput_Cancel, // cancel / close / exit // e.g. Cross (PS4), B (Xbox), A (Switch) - ImGuiNavInput_Input, // text input / on-screen keyboard // e.g. Triang.(PS4), Y (Xbox), X (Switch) - ImGuiNavInput_Menu, // tap: toggle menu / hold: focus, move, resize // e.g. Square (PS4), X (Xbox), Y (Switch) - ImGuiNavInput_DpadLeft, // move / tweak / resize window (w/ PadMenu) // e.g. D-pad Left/Right/Up/Down + ImGuiNavInput_Activate, // activate / open / toggle / tweak value // e.g. Circle (PS4), A (Xbox), B (Switch), Space (Keyboard) + ImGuiNavInput_Cancel, // cancel / close / exit // e.g. Cross (PS4), B (Xbox), A (Switch), Escape (Keyboard) + ImGuiNavInput_Input, // text input / on-screen keyboard // e.g. Triang.(PS4), Y (Xbox), X (Switch), Return (Keyboard) + ImGuiNavInput_Menu, // tap: toggle menu / hold: focus, move, resize // e.g. Square (PS4), X (Xbox), Y (Switch), Alt (Keyboard) + ImGuiNavInput_DpadLeft, // move / tweak / resize window (w/ PadMenu) // e.g. D-pad Left/Right/Up/Down (Gamepads), Arrow keys (Keyboard) ImGuiNavInput_DpadRight, // ImGuiNavInput_DpadUp, // ImGuiNavInput_DpadDown, // - ImGuiNavInput_LStickLeft, // scroll / move window (w/ PadMenu) // e.g. Left Analog Stick Left/Right/Up/Down + ImGuiNavInput_LStickLeft, // scroll / move window (w/ PadMenu) // e.g. Left Analog Stick Left/Right/Up/Down ImGuiNavInput_LStickRight, // ImGuiNavInput_LStickUp, // ImGuiNavInput_LStickDown, // - ImGuiNavInput_FocusPrev, // next window (w/ PadMenu) // e.g. L1 (PS4), LB (Xbox), L (Switch) - ImGuiNavInput_FocusNext, // prev window (w/ PadMenu) // e.g. R1 (PS4), RB (Xbox), R (Switch) - ImGuiNavInput_TweakSlow, // slower tweaks // e.g. L2 (PS4), LT (Xbox), ZL (Switch), Analog - ImGuiNavInput_TweakFast, // faster tweaks // e.g. R2 (PS4), RT (Xbox), ZR (Switch), Analog - // Keyboard Mapping - // [BETA] To use keyboard control you currently need to map keys to those gamepad inputs: PadActivate (Enter), PadCancel (Escape), PadInput (Enter). - // Will add specialized keyboard mappings as we add features and clarify the input interface. - ImGuiNavInput_KeyMenu_, // toggle menu // e.g. Alt - ImGuiNavInput_KeyLeft_, // move left // e.g. Arrow keys + ImGuiNavInput_FocusPrev, // next window (w/ PadMenu) // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) + ImGuiNavInput_FocusNext, // prev window (w/ PadMenu) // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) + ImGuiNavInput_TweakSlow, // slower tweaks // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) + ImGuiNavInput_TweakFast, // faster tweaks // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) + + // [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them. + // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) may be directly reading from io.KeyDown[] instead of io.NavInputs[]. + ImGuiNavInput_KeyMenu_, // toggle menu // = io.KeyAlt + ImGuiNavInput_KeyLeft_, // move left // = Arrow keys ImGuiNavInput_KeyRight_, // move right ImGuiNavInput_KeyUp_, // move up ImGuiNavInput_KeyDown_, // move down ImGuiNavInput_COUNT, + ImGuiNavInput_InternalStart_ = ImGuiNavInput_KeyMenu_ }; // [BETA] Gamepad/Keyboard directional navigation options enum ImGuiNavFlags_ { - ImGuiNavFlags_EnableGamepad = 1 << 0, // Master gamepad navigation enable flag. This is mostly to instruct your imgui binding whether to fill in gamepad navigation inputs. - ImGuiNavFlags_EnableKeyboard = 1 << 1, // Master keyboard navigation enable flag. This is mostly to instruct your imgui binding whether to fill in keyboard navigation inputs. + ImGuiNavFlags_EnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeyDown[]. + ImGuiNavFlags_EnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui back-end to fill io.NavInputs[]. ImGuiNavFlags_MoveMouse = 1 << 2, // Request navigation to allow moving the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantMoveMouse=true. If enabled you MUST honor io.WantMoveMouse requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. ImGuiNavFlags_NoCaptureKeyboard = 1 << 3 // Do not set the io.WantCaptureKeyboard flag with io.NavActive is set. }; @@ -1011,7 +1015,7 @@ struct ImGuiIO bool KeySuper; // Keyboard modifier pressed: Cmd/Super/Windows bool KeysDown[512]; // Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). ImWchar InputCharacters[16+1]; // List of characters input (translated by user from keypress+keyboard state). Fill using AddInputCharacter() helper. - float NavInputs[ImGuiNavInput_COUNT]; + float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs (keyboard keys will be auto-mapped and be written here by ImGui::NewFrame) // Functions IMGUI_API void AddInputCharacter(ImWchar c); // Add new character into InputCharacters[] From ae30efc0fd55bf7015818e512db7aad6bf91cc9a Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Feb 2018 19:58:23 +0100 Subject: [PATCH 318/319] Nav: Examples: Added commented out io.NavFlags |= ImGuiNavFlags_EnableKeyboard to all examples. (#787) --- examples/allegro5_example/main.cpp | 3 ++- examples/directx10_example/main.cpp | 3 ++- examples/directx11_example/main.cpp | 2 +- examples/directx9_example/main.cpp | 3 ++- examples/marmalade_example/main.cpp | 3 ++- examples/opengl2_example/main.cpp | 3 ++- examples/opengl3_example/main.cpp | 4 ++-- examples/sdl_opengl2_example/main.cpp | 3 ++- examples/vulkan_example/main.cpp | 3 ++- 9 files changed, 17 insertions(+), 10 deletions(-) diff --git a/examples/allegro5_example/main.cpp b/examples/allegro5_example/main.cpp index b740dace..99da7492 100644 --- a/examples/allegro5_example/main.cpp +++ b/examples/allegro5_example/main.cpp @@ -23,7 +23,9 @@ int main(int, char**) al_register_event_source(queue, al_get_mouse_event_source()); // Setup ImGui binding + ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui_ImplA5_Init(display); + //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); @@ -36,7 +38,6 @@ int main(int, char**) // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'misc/fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //ImGuiIO& io = ImGui::GetIO(); //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); diff --git a/examples/directx10_example/main.cpp b/examples/directx10_example/main.cpp index aebf81f8..8a3d3f29 100644 --- a/examples/directx10_example/main.cpp +++ b/examples/directx10_example/main.cpp @@ -120,7 +120,9 @@ int main(int, char**) UpdateWindow(hwnd); // Setup ImGui binding + ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui_ImplDX10_Init(hwnd, g_pd3dDevice); + //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); @@ -133,7 +135,6 @@ int main(int, char**) // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'misc/fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //ImGuiIO& io = ImGui::GetIO(); //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index 758ecc2e..48dc53ba 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -125,7 +125,7 @@ int main(int, char**) // Setup ImGui binding ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui_ImplDX11_Init(hwnd, g_pd3dDevice, g_pd3dDeviceContext); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; + //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); diff --git a/examples/directx9_example/main.cpp b/examples/directx9_example/main.cpp index f40e5ef4..5b41ef62 100644 --- a/examples/directx9_example/main.cpp +++ b/examples/directx9_example/main.cpp @@ -75,7 +75,9 @@ int main(int, char**) } // Setup ImGui binding + ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui_ImplDX9_Init(hwnd, g_pd3dDevice); + //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); @@ -88,7 +90,6 @@ int main(int, char**) // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'misc/fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //ImGuiIO& io = ImGui::GetIO(); //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); diff --git a/examples/marmalade_example/main.cpp b/examples/marmalade_example/main.cpp index 58c2182b..dc1db1b2 100644 --- a/examples/marmalade_example/main.cpp +++ b/examples/marmalade_example/main.cpp @@ -15,7 +15,9 @@ int main(int, char**) { // Setup ImGui binding + ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui_Marmalade_Init(true); + //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); @@ -28,7 +30,6 @@ int main(int, char**) // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'misc/fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //ImGuiIO& io = ImGui::GetIO(); //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); diff --git a/examples/opengl2_example/main.cpp b/examples/opengl2_example/main.cpp index 89d41c37..182cc70c 100644 --- a/examples/opengl2_example/main.cpp +++ b/examples/opengl2_example/main.cpp @@ -27,7 +27,9 @@ int main(int, char**) glfwSwapInterval(1); // Enable vsync // Setup ImGui binding + ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui_ImplGlfwGL2_Init(window, true); + //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); @@ -40,7 +42,6 @@ int main(int, char**) // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'misc/fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //ImGuiIO& io = ImGui::GetIO(); //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index 59c60fb6..b5a60f7c 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -34,8 +34,8 @@ int main(int, char**) // Setup ImGui binding ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui_ImplGlfwGL3_Init(window, true); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; - //io.NavFlags |= ImGuiNavFlags_EnableGamepad; + //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls + //io.NavFlags |= ImGuiNavFlags_EnableGamepad; // Enable Gamepad Controls // Setup style ImGui::StyleColorsDark(); diff --git a/examples/sdl_opengl2_example/main.cpp b/examples/sdl_opengl2_example/main.cpp index becf841a..35d28d9c 100644 --- a/examples/sdl_opengl2_example/main.cpp +++ b/examples/sdl_opengl2_example/main.cpp @@ -34,7 +34,9 @@ int main(int, char**) SDL_GL_SetSwapInterval(1); // Enable vsync // Setup ImGui binding + ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui_ImplSdlGL2_Init(window); + //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); @@ -47,7 +49,6 @@ int main(int, char**) // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'misc/fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //ImGuiIO& io = ImGui::GetIO(); //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 41939aab..6a2c342c 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -614,6 +614,7 @@ int main(int, char**) setup_vulkan(window); // Setup ImGui binding + ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui_ImplGlfwVulkan_Init_Data init_data = {}; init_data.allocator = g_Allocator; init_data.gpu = g_Gpu; @@ -623,6 +624,7 @@ int main(int, char**) init_data.descriptor_pool = g_DescriptorPool; init_data.check_vk_result = check_vk_result; ImGui_ImplGlfwVulkan_Init(window, true, &init_data); + //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); @@ -635,7 +637,6 @@ int main(int, char**) // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Read 'misc/fonts/README.txt' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //ImGuiIO& io = ImGui::GetIO(); //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); From 4e5b7612aea7a63aac759f0bf6b26b591bd705ae Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Feb 2018 20:05:21 +0100 Subject: [PATCH 319/319] Nav: Documentation tweaks. (#787) --- imgui.cpp | 49 +++++++++++++++++++++++++++---------------------- imgui.h | 10 +++------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3895c6c9..ec2ae3d5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -81,7 +81,6 @@ - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!) - Controls are automatically adjusted for OSX to match standard OSX text editing operations. - Gamepad navigation: see suggested mappings in imgui.h ImGuiNavInput_ - - Keyboard navigation: see suggested mappings in imgui.h ImGuiNavInput_ PROGRAMMER GUIDE @@ -212,28 +211,34 @@ USING GAMEPAD/KEYBOARD NAVIGATION [BETA] - - Ask questions and report issues at https://github.com/ocornut/imgui/issues/787. + - Ask questions and report issues at https://github.com/ocornut/imgui/issues/787 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. - - Your inputs are passed to imgui by filling the io.NavInputs[] array. See 'enum ImGuiNavInput_' in imgui.h for a description of available inputs. - - Please refer to the examples/ application for suggested keyboard and gamepad mapping. - - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. - - Consoles/Tablet/Phone users: Consider using Synergy host (on your computer) + uSynergy.c (in your console/tablet/phone app) to use your PC mouse/keyboard. - - The ImGuiNavFlags_EnableGamepad and ImGuiNavFlags_EnableKeyboard flags of io.NavFlags are only here to instruct your binding whether to find inputs. - - For gamepad use, the easiest approach is to go all-or-nothing, with a buttons combo that toggle your inputs between imgui and your game/application. - Sharing inputs in a more advanced or granular way between imgui and your game/application may be tricky and requires further work on imgui. - When keyboard navigation is active (io.NavActive + NavFlags_EnableKeyboard), the io.WantCaptureKeyboard is set. - For more advanced uses, you may want to use: - - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. - - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). - - query focus information with e.g. IsWindowFocused(), IsItemFocused() etc. functions. - Please reach out if you think the game vs navigation input sharing could be improved. - - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiNavFlags_MoveMouse flag in io.NavFlags. - Enabling ImGuiNavFlags_MoveMouse instructs dear imgui to move your mouse cursor along with navigation movement. - When enabled, the NewFrame() functions may alter 'io.MousePos' and set 'io.WantMoveMouse' to notify you that it did so. - When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that. - (If you set the ImGuiNavFlags_MoveMouse flag but don't honor 'io.WantMoveMouse' properly, imgui will misbehave as it will see your mouse as moving back and forth.) - (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want - to set a boolean to ignore your other external mouse positions until the external source is moved again.) + - Keyboard: + - Set io.NavFlags |= ImGuiNavFlags_EnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays. + - When keyboard navigation is active (io.NavActive + NavFlags_EnableKeyboard), the io.WantCaptureKeyboard flag will be set. + For more advanced uses, you may want to read from: + - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. + - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). + - or query focus information with e.g. IsWindowFocused(), IsItemFocused() etc. functions. + Please reach out if you think the game vs navigation input sharing could be improved. + - Gamepad: + - Set io.NavFlags |= ImGuiNavFlags_EnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). + - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values: + 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. + - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. + Your code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, maybe a power curve, etc.). + - If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo to toggle the target. + Please reach out if you think the game vs navigation input sharing could be improved. + - Mouse: + - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. + - Consoles/Tablet/Phone users: Consider using Synergy host (on your computer) + uSynergy.c (in your console/tablet/phone app) to use your PC mouse/keyboard. + - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiNavFlags_MoveMouse flag in io.NavFlags. + Enabling ImGuiNavFlags_MoveMouse instructs dear imgui to move your mouse cursor along with navigation movements. + When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantMoveMouse' to notify you that it wants the mouse cursor to be moved. + When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that. + (If you set the ImGuiNavFlags_MoveMouse flag but don't honor 'io.WantMoveMouse' properly, imgui will misbehave as it will see your mouse as moving back and forth.) + (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want + to set a boolean to ignore your other external mouse positions until the external source is moved again.) API BREAKING CHANGES diff --git a/imgui.h b/imgui.h index c2163640..f4bdb6de 100644 --- a/imgui.h +++ b/imgui.h @@ -704,13 +704,9 @@ enum ImGuiKey_ }; // [BETA] Gamepad/Keyboard directional navigation -// Keyboard: -// - io.NavInputs[] is automatically filled in by NewFrame() if you set io.NavFlags |= ImGuiNavFlags_EnableKeyboard. -// Gamepad: -// - Fill io.NavInputs[] every frame with your gamepad inputs. Note that io.NavInputs[] is _cleared_ in EndFrame(). -// 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. -// - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. -// Your code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, maybe a power curve, etc.). +// Keyboard: Set io.NavFlags |= ImGuiNavFlags_EnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays. +// Gamepad: Set io.NavFlags |= ImGuiNavFlags_EnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). +// Read instructions in imgui.cpp for more details. enum ImGuiNavInput_ { // Gamepad Mapping