-
Notifications
You must be signed in to change notification settings - Fork 53
Named References
- What is ImGuiTestRef?
- Basic usage
- Using
SetRef()
to set a Base Reference - Using the
//
prefix to use an absolute reference (ignore Base Reference) - Using the
/
prefix to use a window-relative reference - Using the
//$FOCUSED
prefix - Using
**/
wildcards to search by full label - Using
$$123
to easily encode PushID() integers in a string - Using
WindowInfo()
to easily access child windows - Accessing animated/localized
###
identifiers
Most functions in the ImGuiTestContext
API are taking a ImGuiTestRef
argument in order to refer to an item/widget.
void ItemClick(ImGuiTestRef ref, ImGuiMouseButton button = 0);
Typical usage:
ctx->ItemClick("Button");
A ImGuiTestRef
is typically constructed implicitly from another type and:
- can hold the named path to an item/window (when constructed from a
const char*
) - can hold the ID of an item/window (when constructed from a
ImGuiID
)
Using named paths is greatly convenient and open many possibilities.
The structure itself is pretty lightweight and straightforward (see its definition). Its short lifetime as function argument means it only holds a pointer to a string.
TL;DR; examples:
ctx->SetRef("Window"); // Set Base Reference
ctx->ItemClick("Button"); // Relative path
ctx->ItemClick("Node/Item"); // Relative path (composite)
ctx->ItemClick("//Window/Button"); // Absolute path
ctx->ItemClick("//$FOCUSED/Button"); // Relative to focused window
ctx->ItemClick("//Window/List/$$0/Button"); // Encode literal integer in string, for PushID(int)
ctx->ItemClick("//Window/**/Button"); // Wildcard search by full label
ctx->WindowInfo("//Window/Child/SubChild"); // Find child window by short name
Consider this GUI code:
ImGui::Begin("Window");
ImGui::Button("Button");
if (ImGui::TreeNode("Node"))
{
ImGui::Checkbox("Checkbox", &b);
ImGui::TreePop();
}
ImGui::End();
In Test code, there are different ways to access those items with named paths:
ctx->ItemClick("//Window/Button"); // Absolute path to the button
ctx->ItemCheck("//Window/Node/Checkbox"); // Absolute path to the checkbox
Single forward slashes /
are used as a natural delimiters for sections of a path. Fun Fact: Because the underlying identifiers used by Dear ImGui are hashes of concatenated data, the single forward slashes are technically unnecessary (but absolutely recommended for readability):
ctx->ItemCheck("//WindowNodeCheckbox"); // Also works! (but not recommended: it is bizarre and harder to read!)
In case you need to refer to an item containing a slash in its name, you'll need to escape it using a backslash \
. In the majority of programming languages, backslashes in literal strings themselves needs to be escaped, therefore needing two:
ctx->ItemClick("A\\/B"); // Click item called "A/B"
In order to simplify testing code, it is common to use SetRef()
to set a Base Reference:
ctx->SetRef("//Window"); // Relative paths after this will be based over "//Window".
ctx->ItemClick("Button"); // Relative path to the button (from base provided to SetRef() call)
ctx->ItemCheck("Node/Checkbox"); // Relative path to the checkbox (from base provided to SetRef() call)
ctx->ItemClick("//Window/Button"); // Absolute path to the button
As SetRef()
is often set to a window, it is common that window-related calls use an empty ImGuiTestRef
argument:
ctx->SetRef("SomeWindow");
ctx->WindowResize("", ImVec2(100,100)); // Resize "SomeWindow"
ctx->WindowResize("//SomeWindow", ImVec2(100,100)); // Also resize "SomeWindow"
But the value passed to SetRef()
doesn't necessarily have to be a window:
ctx->SetRef("Window/Node");
ctx->ItemCheck("Checkbox"); // Click "//Window/Node/Checkbox"
And this bizarre/unnatural example illustrate a property of the hashing:
ctx->SetRef("Window/Node/Chec");
ctx->ItemCheck("kbox"); // Click "//Window/Node/Checkbox" (bizarre and strongly discouraged!)
As a convenience, a signature variant of SetRef()
can take ImGuiWindow*
:
ImGuiWindow* some_window = ...;
ctx->SetRef(some_window); // Same as using ctx->SetRef(some_window ? some_window->ID : 0);
It is also provided because ctx->SetRef(some_window->Name)
won't work on most child windows... because they commonly include /
in their name which will be incorrectly be treated as an escape characters (will aim to change that in the main library). See WindowInfo() section for details:
ctx->SetRef("ParentWindow/ChildWindow"); // This currently does not work
ctx->SetRef(ctx->WindowInfo("ParentWinow/ChildWindow")->Window); // This works
By using a leading //
in a named paths, the value set in SetRef()
is ignored.
Consider this GUI code:
ImGui::Begin("Window 1");
ImGui::Button("Button 1");
if (ImGui::TreeNode("Node"))
{
ImGui::Checkbox("Checkbox", &b);
ImGui::TreePop();
}
ImGui::End();
ImGui::Begin("Window 2");
ImGui::Button("Button 2");
ImGui::End();
And this Test code:
// Default base reference is null so we can omit leading `//`
ctx->ItemClick("Window 1/Button 1"); // OK (relative path assuming SetRef() value is 0)
ctx->ItemClick("Window 2/Button 2"); // OK (relative path assuming SetRef() value is 0)
ctx->ItemClick("//Window 1/Button 1"); // OK (absolute path)
ctx->ItemClick("//Window 2/Button 2"); // OK (absolute path)
ctx->SetRef("Window 1");
ctx->ItemClick("Button 1"); // OK (relative path)
ctx->ItemClick("Button 2"); // INCORRECT (relative path will lead to "//Window 1/Button 2" which doesn't exist)
ctx->ItemClick("//Window 1/Button 1"); // OK (absolute path, unnecessary here but works)
ctx->ItemClick("//Window 2/Button 2"); // OK (absolute path)
As a special measure, all values passed to SetRef()
are always considered an absolute reference, never a relative one.
So those two statements are always equivalent, regardless of previous SetRef()
calls:
ctx->SetRef("Window");
ctx->SetRef("//Window");
This is very rarely used. But when using composite path, a leading single-slash /
may be used to get to root of the window contained in the composite path:
ctx->SetRef("Window/Node");
ctx->ItemClick("Button"); // INCORRECT: Attempt to click "//Window/Node/Button" which doesn't exist.
ctx->ItemClick("/Button"); // Click "//Window/Button"
ctx->ItemCheck("Checkbox"); // Click "//Window/Node/Checkbox"
ctx->ItemCheck("/Node/Checkbox"); // Same as above.
ctx->ItemCheck("//Window/Node/Checkbox"); // Same as above.
(As a future extension, we may add support for leading ../
sequences to move up one level in the Base Reference)
By using a leading //$FOCUSED
in a named path, it will be replaced by the name of the currently focused window.
ctx->ItemClick("Open Popup"); // Click on a button to open a popup
ctx->SetRef("//$FOCUSED"); // Set our Base Reference as the newly focused window (assuming it is the popup)
ctx->ItemClick("OK"); // Click OK in the popup
ctx->WindowClose("//$FOCUSED"); // Close the focused window
Important: A SetRef()
call evaluates and locks the current //$FOCUSED
value at the time of the call:
ctx->WindowFocus("Window 1"); // Focus window
ctx->SetRef("//$FOCUSED"); // Set Base Reference to "Window 1" ("//$FOCUSED" is evaluated this point)
ctx->ItemClick("Button 1"); // Click "//Window 1/Button 1"
ctx->WindowFocus("Window 2"); // Focus another window
ctx->ItemClick("Button 1"); // Click "//Window 1/Button 1" (NOT "//$FOCUSED/Button 1" which would be "//Window 2/Button 1");
// But outside of SetRef() it will be continuously reevaluated for each call:
ctx->ItemClick("//$FOCUSED/Button 1"); // INCORRECT: Click "//Window 2/Button 1"
Note that we INTENTIONALLY do not provide the equivalent //$HOVERED
shortcut to refer to the window hovered by mouse.
Why? Because it would be extremely fragile to use! While //$FOCUSED
is technically subject to the same issue, it changes much less frequently and generally following explicit user actions. In our attempts to use //$HOVERED
it led us to so much confusion and bugs we decided it wasn't worth it. You can however use:
IM_CHECK(g.Hoveredwindow != NULL);
ctx->SetRef(g.HoveredWindow->ID);
(This is only supported for visible/unclipped items)
Consider this GUI code:
ImGui::Begin("Window");
ImGui::PushID("something");
ImGui::PushID(42);
ImGui::Button("Button");
ImGui::PopID();
ImGui::PopID();
ImGui::End();
Instead of providing a full named path e.g. //Window/something/$$42/Button
you may use the **/
wildcard system:
ctx->ItemClick("//Window/**/Button");
We specifically refer to "full label" because you cannot use this to do a partial match:
ctx->ItemClick("//Window/**utton"); // Will fail
You can use **/
to cross through child windows boundaries:
ImGui::Begin("Window");
ImGui::BeginChild("Canvas");
ImGui::Button("Button");
ImGui::EndChild();
ImGui::End();
ctx->Setref("Window");
ctx->ItemClick("**/Button"); // OK. Will click "Button" in the child window.
Very convenient: you can use **/
to find an item and then infer the window from it:
ctx->SetRef(ctx->ItemInfo("//Window/**/Button").Window); // Set ref to the child window
While many identifiers pushed in the Dear ImGui ID stack are strings, it is also common to push integers and pointers when displaying lists, tables and tree nodes.
// Submit 10 buttons
ImGui::Begin("List");
for (int n = 0; n < 10; n++)
{
ImGui::PushID(n);
ImGui::Button("Button");
ImGui::PopID();
}
ImGui::End();
As a convenience we provide a syntax to encode numbers/literals into named paths:
ctx->ItemClick("//List/$$5/Button"); // Click the 6th button
An encoded literal must constitute the entirety of a section of the named path. //List/$$5something
is incorrect and will error, whereas //List/$$5/something
is correct. This is an exception to the suggestion above that single forward slash may be omitted.
Dear ImGui provides functions pushing pointer to the ID stack (e.g. PushID(void*)
, TreeNode(void*, const char* text)
). We expect that using them will make things more complicated in term of accessing items by a unique path name. Out of completeness we however have way to encode pointers using the $$(type)value
syntax:
MyNode* node = (MyMode*)0x1234FFFF;
ImGui::PushID(node);
ImGui::Button("Button");
ImGui::PopID();
ctx->ItemClick("//Window/$$(ptr)0x1234FFFF/Button"); // Click the button
Note that involving pointer in named paths is likely counterproductive. Some alternatives:
- Use
**/label
wildcard search to find uniquely label items without knowing their full path. - Use queries such as
ctx->GatherItems()
to list and scan through items in a hierarchy.
Child windows internally have mangled names (for reasons outside of the scope of this article). Consider this GUI code:
ImGui::Begin("Window");
ImGui::BeginChild("Child");
ImGui::Button("Button");
ImGui::EndChild();
ImGui::End();
Unfortunately because of the mangling:
// Currently doesn't work
ctx->ItemClick("Window/Child/Button");
// Works
ctx->SetRef(ctx->WindowInfo("Window/Child")->Window");
ctx->SetRef("Button");
We are working on finding ways to simplify this further.
Consider this GUI code:
ImGui::Button("ボタン###button1"); // Label is "ボタン", ID is "###button"
On Dear ImGui side, the ###
operator is used to distinguish a visible label from its identifier.
To refer to those identifiers you need to include the ###
prefix in your named path:
ctx->ItemClick("button1"); // INCORRECT
ctx->ItemClick("###button1"); // OK
ctx->ItemClick("ボタン###button1"); // OK (but unnecessary)
This constraint may be lifted in the future (without endangering backward compatibility).
In contrast, there's nothing special to consider when using ##
operator:
ImGui::Button("hello##button2"); // Label is "hello", ID is "hello##button"
ctx->ItemClick("hello##button2"); // OK. As per the definition of ##, the label is included in ID computation.