-
Notifications
You must be signed in to change notification settings - Fork 39
Named References
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
can hold either the path to an item/window, either a precomputed item/window identifier (ImGuiID
which is hash of an absolute path).
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.
Consider this GUI code:
ImGui::Begin("Window");
ImGui::Button("Button");
if (ImGui::TreeNode("Node"))
{
ImGui::Checkbox("Checkbox", &b);
ImGui::TreePop();
}
ImGui::End();
Different ways to access the button and checkbox 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 delimitation for items in 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 item containing a slash in their name, you'll need to escape it using a backslash \
. Note that 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 Base Reference:
ctx->SetRef("//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
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 absolute reference, never relative.
So those two statements are always equivalent:
ctx->SetRef("Window");
ctx->SetRef("//Window");
The value passed to SetRef()
doesn't necessarily has to be a window:
ctx->SetRef("Window/Node");
ctx->ItemCheck("Checkbox");
And this bizarre/unnatural example illustrate a property of the hashing:
ctx->SetRef("Window/Node/Chec");
ctx->ItemCheck("kbox"); // Click "Window/Node/Checkbox": Strongly discouraged!
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"); // 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 trailing ../
sequences to move up one level in the Base Reference)
Using a trailing //$FOCUSED
syntax is a shortcut to access the identifier 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: using //$FOCUSED
inside a SetRef()
calls evaluate and locks the currently focused value at the time of the SetRef()
call:
ctx->WindowFocus("Window 1"); // Focus window
ctx->SetRef("//$FOCUSED"); // Set Base Reference to "Window 1" (`//$FOCUSED` is "translated" at 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 simply use:
IM_CHECK(g.Hoveredwindow != NULL);
ctx->SetRef(g.HoveredWindow->ID);