Gamepad/Keyboard navigation support, initial commit, WIP (#323)

This commit is contained in:
ocornut 2016-07-20 00:02:59 +02:00
parent 4a11cc35b9
commit c2cb2a6928
4 changed files with 878 additions and 117 deletions

877
imgui.cpp

File diff suppressed because it is too large Load Diff

23
imgui.h
View File

@ -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]; // <unset> // 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; // <auto> // 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()

View File

@ -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();

View File

@ -13,6 +13,7 @@
#include <stdio.h> // FILE*
#include <math.h> // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf
#include <limits.h> // 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<ImGuiIniData> Settings; // .ini Settings
@ -382,6 +403,29 @@ struct ImGuiContext
ImVector<ImGuiPopupRef> OpenPopupStack; // Which popups are open (persistent)
ImVector<ImGuiPopupRef> 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<ImGuiWindow*> 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<float> ItemWidthStack;
ImVector<float> 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<ImGuiID> 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); }