mirror of https://github.com/ocornut/imgui
Nav: Tabbing now cycles through all items when ImGuiConfigFlags_NavEnableKeyboard is set. (#3092, #5759, #787)
This commit is contained in:
parent
e83fb468c6
commit
2bb9e35a48
|
@ -53,6 +53,10 @@ Breaking Changes:
|
|||
|
||||
Other changes:
|
||||
|
||||
- Nav: Tabbing now cycles through all items when ImGuiConfigFlags_NavEnableKeyboard is set.
|
||||
(#3092, #5759, #787)
|
||||
- Nav: Tabbing/Shift-Tabbing can more reliably be used to step out of an item that is not
|
||||
tab-stoppable. (#3092, #5759, #787)
|
||||
- Nav: Made Enter key submit the same type of Activation event as Space key,
|
||||
allowing to press buttons with Enter. (#5606)
|
||||
(Enter emulates a "prefer text input" activation vs.
|
||||
|
|
170
imgui.cpp
170
imgui.cpp
|
@ -39,14 +39,13 @@ Index of this file:
|
|||
DOCUMENTATION
|
||||
|
||||
- MISSION STATEMENT
|
||||
- END-USER GUIDE
|
||||
- CONTROLS GUIDE
|
||||
- PROGRAMMER GUIDE
|
||||
- READ FIRST
|
||||
- HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
|
||||
- GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
|
||||
- HOW A SIMPLE APPLICATION MAY LOOK LIKE
|
||||
- HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
|
||||
- USING KEYBOARD/GAMEPAD NAVIGATION CONTROLS
|
||||
- API BREAKING CHANGES (read me when you update!)
|
||||
- FREQUENTLY ASKED QUESTIONS (FAQ)
|
||||
- Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
|
||||
|
@ -114,27 +113,73 @@ CODE
|
|||
- Limited layout features, intricate layouts are typically crafted in code.
|
||||
|
||||
|
||||
END-USER GUIDE
|
||||
CONTROLS GUIDE
|
||||
==============
|
||||
|
||||
- Double-click on 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 (double-click to auto fit window to its contents).
|
||||
- Click and drag on any empty space to move window.
|
||||
- TAB/SHIFT+TAB to cycle through keyboard editable fields.
|
||||
- CTRL+Click on a slider or drag box to input value as text.
|
||||
- Use mouse wheel to scroll.
|
||||
- Text editor:
|
||||
- Hold SHIFT or use mouse to select text.
|
||||
- CTRL+Left/Right to word jump.
|
||||
- CTRL+Shift+Left/Right to select words.
|
||||
- CTRL+A or Double-Click to select all.
|
||||
- CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
|
||||
- CTRL+Z,CTRL+Y to undo/redo.
|
||||
- ESCAPE to revert text to its original value.
|
||||
- Controls are automatically adjusted for OSX to match standard OSX text editing operations.
|
||||
- General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
|
||||
- General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. Download controller mapping PNG/PSD at http://dearimgui.org/controls_sheets
|
||||
- MOUSE CONTROLS
|
||||
- Mouse wheel: Scroll vertically.
|
||||
- SHIFT+Mouse wheel: Scroll horizontally.
|
||||
- Click [X]: Close a window, available when 'bool* p_open' is passed to ImGui::Begin().
|
||||
- Click ^, Double-Click title: Collapse window.
|
||||
- Drag on corner/border: Resize window (double-click to auto fit window to its contents).
|
||||
- Drag on any empty space: Move window (unless io.ConfigWindowsMoveFromTitleBarOnly = true).
|
||||
- Left-click outside popup: Close popup stack (right-click over underlying popup: Partially close popup stack).
|
||||
|
||||
- TEXT EDITOR
|
||||
- Hold SHIFT or Drag Mouse: Select text.
|
||||
- CTRL+Left/Right: Word jump.
|
||||
- CTRL+Shift+Left/Right: Select words.
|
||||
- CTRL+A or Double-Click: Select All.
|
||||
- CTRL+X, CTRL+C, CTRL+V: Use OS clipboard.
|
||||
- CTRL+Z, CTRL+Y: Undo, Redo.
|
||||
- ESCAPE: Revert text to its original value.
|
||||
- On OSX, controls are automatically adjusted to match standard OSX text editing shortcuts and behaviors.
|
||||
|
||||
- KEYBOARD CONTROLS
|
||||
- Basic:
|
||||
- Tab, SHIFT+Tab Cycle through text editable fields.
|
||||
- CTRL+Tab, CTRL+Shift+Tab Cycle through windows.
|
||||
- CTRL+Click Input text into a Slider or Drag widget.
|
||||
- Extended features with `io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard`:
|
||||
- Tab, SHIFT+Tab: Cycle through every items.
|
||||
- Arrow keys Move through items using directional navigation. Tweak value.
|
||||
- Arrow keys + Alt, Shift Tweak slower, tweak faster (when using arrow keys).
|
||||
- Enter Activate item (prefer text input when possible).
|
||||
- Space Activate item (prefer tweaking with arrows when possible).
|
||||
- Escape Deactivate item, leave child window, close popup.
|
||||
- Page Up, Page Down Previous page, next page.
|
||||
- Home, End Scroll to top, scroll to bottom.
|
||||
- Alt Toggle between scrolling layer and menu layer.
|
||||
- CTRL+Tab then Ctrl+Arrows Move window. Hold SHIFT to resize instead of moving.
|
||||
- Output when ImGuiConfigFlags_NavEnableKeyboard set,
|
||||
- io.WantCaptureKeyboard flag is set when keyboard is claimed.
|
||||
- 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 (usually goes to back false when mouse is used).
|
||||
|
||||
- GAMEPAD CONTROLS
|
||||
- Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||
- Particularly useful to use Dear ImGui on a console system (e.g. PlayStation, Switch, Xbox) without a mouse!
|
||||
- Download controller mapping PNG/PSD at http://dearimgui.org/controls_sheets
|
||||
- Backend support: backend needs to:
|
||||
- Set 'io.BackendFlags |= ImGuiBackendFlags_HasGamepad' + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys.
|
||||
- For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly.
|
||||
Backend 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, etc.).
|
||||
- BEFORE 1.87, BACKENDS USED TO WRITE TO io.NavInputs[]. This is now obsolete. Please call io functions instead!
|
||||
- If you need to share inputs between your game and the Dear ImGui interface, 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.
|
||||
|
||||
- REMOTE INPUTS SHARING & MOUSE EMULATION
|
||||
- PS4/PS5 users: Consider emulating a mouse cursor with DualShock touch pad or a spare analog stick as a mouse-emulation fallback.
|
||||
- Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + run examples/libs/synergy/uSynergy.c (on your console/tablet/phone app)
|
||||
in order to share your PC mouse/keyboard.
|
||||
- See https://github.com/ocornut/imgui/wiki/Useful-Extensions#remoting for other remoting solutions.
|
||||
- On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavEnableSetMousePos flag.
|
||||
Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs Dear ImGui to move your mouse cursor along with navigation movements.
|
||||
When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved.
|
||||
When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the backends in examples/ do that.
|
||||
(If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, Dear ImGui will misbehave as it will see your mouse moving back & 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.)
|
||||
|
||||
|
||||
PROGRAMMER GUIDE
|
||||
|
@ -344,41 +389,6 @@ CODE
|
|||
}
|
||||
|
||||
|
||||
USING KEYBOARD/GAMEPAD NAVIGATION CONTROLS
|
||||
------------------------------------------
|
||||
- The keyboard/gamepad navigation is fairly functional and keeps being improved.
|
||||
- Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PlayStation, Switch, Xbox) without a mouse!
|
||||
- The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
|
||||
- Keyboard:
|
||||
- Application: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
|
||||
- Arrow keys to move. Space or Enter to activate (on a slider/drag: Space will tweak with arrow, Enter will input text).
|
||||
- When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard),
|
||||
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(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
|
||||
Please reach out if you think the game vs navigation input sharing could be improved.
|
||||
- Gamepad:
|
||||
- Application: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
|
||||
- Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys.
|
||||
For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly.
|
||||
Backend 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, etc.).
|
||||
- BEFORE 1.87, BACKENDS USED TO WRITE TO io.NavInputs[]. This is now obsolete. Please call io functions instead!
|
||||
- You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://dearimgui.org/controls_sheets
|
||||
- If you need to share inputs between your game and the Dear ImGui interface, 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/PS5 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 a Synergy 1.x server (on your PC) + uSynergy.c (on your console/tablet/phone app) to share 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 ImGuiConfigFlags_NavEnableSetMousePos flag.
|
||||
Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
|
||||
When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved.
|
||||
When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the backends in examples/ do that.
|
||||
(If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse 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
|
||||
====================
|
||||
|
||||
|
@ -1013,7 +1023,7 @@ static void NavEndFrame();
|
|||
static bool NavScoreItem(ImGuiNavItemData* result);
|
||||
static void NavApplyItemToResult(ImGuiNavItemData* result);
|
||||
static void NavProcessItem();
|
||||
static void NavProcessItemForTabbingRequest(ImGuiID id);
|
||||
static void NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags);
|
||||
static ImVec2 NavCalcPreferredRefPos();
|
||||
static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
|
||||
static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
|
||||
|
@ -10746,16 +10756,14 @@ static void ImGui::NavProcessItem()
|
|||
|
||||
// Process Move Request (scoring for navigation)
|
||||
// FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)
|
||||
if (g.NavMoveScoringItems)
|
||||
if (g.NavMoveScoringItems && (item_flags & ImGuiItemFlags_Disabled) == 0)
|
||||
{
|
||||
const bool is_tab_stop = (item_flags & ImGuiItemFlags_Inputable) && (item_flags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
|
||||
const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) != 0;
|
||||
if (is_tabbing)
|
||||
{
|
||||
if (is_tab_stop || (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi))
|
||||
NavProcessItemForTabbingRequest(id);
|
||||
NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags);
|
||||
}
|
||||
else if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_Disabled))
|
||||
else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId))
|
||||
{
|
||||
ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
|
||||
if (NavScoreItem(result))
|
||||
|
@ -10789,18 +10797,31 @@ static void ImGui::NavProcessItem()
|
|||
// - Case 3: tab forward wrap: set result to first eligible item (preemptively), on ref id set counter, on next frame if counter hasn't elapsed store result. // FIXME-TABBING: Could be done as a next-frame forwarded request
|
||||
// - Case 4: tab backward: store all results, on ref id pick prev, stop storing
|
||||
// - Case 5: tab backward wrap: store all results, on ref id if no result keep storing until last // FIXME-TABBING: Could be done as next-frame forwarded requested
|
||||
void ImGui::NavProcessItemForTabbingRequest(ImGuiID id)
|
||||
void ImGui::NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
|
||||
if ((move_flags & ImGuiNavMoveFlags_FocusApi) == 0)
|
||||
if (g.NavLayer != g.CurrentWindow->DC.NavLayerCurrent)
|
||||
return;
|
||||
|
||||
// - Can always land on an item when using API call.
|
||||
// - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item.
|
||||
// - Tabbing without _NavEnableKeyboard: goes through inputable items only.
|
||||
bool can_stop;
|
||||
if (move_flags & ImGuiNavMoveFlags_FocusApi)
|
||||
can_stop = true;
|
||||
else
|
||||
can_stop = (item_flags & ImGuiItemFlags_NoTabStop) == 0 && ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) || (item_flags & ImGuiItemFlags_Inputable));
|
||||
|
||||
// Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows)
|
||||
ImGuiNavItemData* result = &g.NavMoveResultLocal;
|
||||
if (g.NavTabbingDir == +1)
|
||||
{
|
||||
// Tab Forward or SetKeyboardFocusHere() with >= 0
|
||||
if (g.NavTabbingResultFirst.ID == 0)
|
||||
if (can_stop && g.NavTabbingResultFirst.ID == 0)
|
||||
NavApplyItemToResult(&g.NavTabbingResultFirst);
|
||||
if (--g.NavTabbingCounter == 0)
|
||||
if (can_stop && g.NavTabbingCounter > 0 && --g.NavTabbingCounter == 0)
|
||||
NavMoveRequestResolveWithLastItem(result);
|
||||
else if (g.NavId == id)
|
||||
g.NavTabbingCounter = 1;
|
||||
|
@ -10816,16 +10837,18 @@ void ImGui::NavProcessItemForTabbingRequest(ImGuiID id)
|
|||
NavUpdateAnyRequestFlag();
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (can_stop)
|
||||
{
|
||||
// Keep applying until reaching NavId
|
||||
NavApplyItemToResult(result);
|
||||
}
|
||||
}
|
||||
else if (g.NavTabbingDir == 0)
|
||||
{
|
||||
// Tab Init
|
||||
if (g.NavTabbingResultFirst.ID == 0)
|
||||
NavMoveRequestResolveWithLastItem(&g.NavTabbingResultFirst);
|
||||
if (can_stop && g.NavId == id)
|
||||
NavMoveRequestResolveWithLastItem(result);
|
||||
if (can_stop && g.NavTabbingResultFirst.ID == 0) // Tab init
|
||||
NavApplyItemToResult(&g.NavTabbingResultFirst);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11348,8 +11371,11 @@ void ImGui::NavUpdateCreateTabbingRequest()
|
|||
// (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!)
|
||||
// Initially this was designed to use counters and modulo arithmetic, but that could not work with unsubmitted items (list clipper). Instead we use a strategy close to other move requests.
|
||||
// See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping.
|
||||
//// FIXME: We use (g.ActiveId == 0) but (g.NavDisableHighlight == false) might be righter once we can tab through anything
|
||||
g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1;
|
||||
const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
|
||||
if (nav_keyboard_active)
|
||||
g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavDisableHighlight == true && g.ActiveId == 0) ? 0 : +1;
|
||||
else
|
||||
g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1;
|
||||
ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
|
||||
ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down;
|
||||
NavMoveRequestSubmit(ImGuiDir_None, clip_dir, ImGuiNavMoveFlags_Tabbing, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
|
||||
|
@ -11369,7 +11395,7 @@ void ImGui::NavMoveRequestApplyResult()
|
|||
ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL;
|
||||
|
||||
// Tabbing forward wrap
|
||||
if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing)
|
||||
if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && result == NULL)
|
||||
if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID)
|
||||
result = &g.NavTabbingResultFirst;
|
||||
|
||||
|
|
2
imgui.h
2
imgui.h
|
@ -1495,7 +1495,7 @@ enum ImGuiNavInput
|
|||
enum ImGuiConfigFlags_
|
||||
{
|
||||
ImGuiConfigFlags_None = 0,
|
||||
ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag.
|
||||
ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. Enable full Tabbing + directional arrows + space/enter to activate.
|
||||
ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. Backend also needs to set ImGuiBackendFlags_HasGamepad.
|
||||
ImGuiConfigFlags_NavEnableSetMousePos = 1 << 2, // Instruct 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.WantSetMousePos=true. If enabled you MUST honor io.WantSetMousePos requests in your backend, otherwise ImGui will react as if the mouse is jumping around back and forth.
|
||||
ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // Instruct navigation to not set the io.WantCaptureKeyboard flag when io.NavActive is set.
|
||||
|
|
Loading…
Reference in New Issue