Небольшое расширение редактора узлов без зависимостей для дорогого imgui.
Imnodes стремится предоставить простой интерфейс немедленного режима для создания редактора узлов в окне ImGui. Imnodes предоставляет простые, настраиваемые строительные блоки, необходимые пользователю для создания своего редактора узлов.
Функции:
imnodes.h
, imnodes_internal.h
и imnodes.cpp
в свой проект вместе с ImGui. Этот репозиторий включает несколько файлов примеров в разделе example/
. Они задуманы как простые примеры, дающие представление о том, что можно создать с помощью imnodes.
Если вам нужно создать примеры, вы можете использовать для этого предоставленный сценарий CMake.
# Initialize the vcpkg submodule
$ git submodule update --init
# Run the generation step and build
$ cmake -B build-release/ -S . -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake
$ cmake --build build-release -- -j
Обратите внимание, что это не тестировалось в Linux и, скорее всего, не будет работать на этой платформе.
Вот небольшой обзор того, как используется расширение. Для получения дополнительной информации о примере использования прокрутите файл README до конца.
Прежде чем что-либо можно будет сделать, необходимо инициализировать библиотеку. Это можно сделать одновременно с инициализацией dear imgui
.
ImGui::CreateContext ();
ImNodes::CreateContext ();
// elsewhere in the code...
ImNodes::DestroyContext ();
ImGui::DestroyContext ();
Редактор узлов — это рабочая область, содержащая узлы. Редактор узлов должен быть создан в окне, как и любой другой элемент пользовательского интерфейса.
ImGui::Begin ( " node editor " );
ImNodes::BeginNodeEditor ();
ImNodes::EndNodeEditor ();
ImGui::End ();
Теперь у вас должно быть рабочее пространство с видимой в окне сеткой. Теперь можно создать экземпляр пустого узла:
const int hardcoded_node_id = 1 ;
ImNodes::BeginNodeEditor ();
ImNodes::BeginNode (hardcoded_node_id);
ImGui::Dummy (ImVec2( 80 . 0f , 45 . 0f ));
ImNodes::EndNode ();
ImNodes::EndNodeEditor ();
Узлы, как и окна в dear imgui
должны быть однозначно идентифицированы. Но мы не можем использовать названия узлов для идентификации, поскольку в рабочей области должно быть возможно иметь много узлов с одинаковыми именами. Вместо этого вы просто используете целые числа для идентификации.
Атрибуты — это содержимое пользовательского интерфейса узла. Атрибут будет иметь булавку (маленький кружок) по обе стороны от узла. Существует два типа атрибутов: входные и выходные атрибуты. Выводы входных атрибутов находятся на левой стороне узла, а выводы выходных атрибутов — справа. Как и узлы, выводы должны быть однозначно идентифицированы.
ImNodes::BeginNode (hardcoded_node_id);
const int output_attr_id = 2 ;
ImNodes::BeginOutputAttribute (output_attr_id);
// in between Begin|EndAttribute calls, you can call ImGui
// UI functions
ImGui::Text ( " output pin " );
ImNodes::EndOutputAttribute ();
ImNodes::EndNode ();
Расширению на самом деле все равно, что находится в атрибуте. Он просто отображает пин для атрибута и позволяет пользователю создавать связи между пинами.
Строку заголовка можно добавить к узлу с помощью BeginNodeTitleBar
и EndNodeTitleBar
. Как и в случае с атрибутами, вы помещаете содержимое строки заголовка между вызовами функций. Обратите внимание, что эти функции необходимо вызывать перед добавлением атрибутов или других dear imgui
в узел, поскольку макет узла строится по порядку, сверху вниз.
ImNodes::BeginNode (hardcoded_node_id);
ImNodes::BeginNodeTitleBar ();
ImGui::TextUnformatted ( " output node " );
ImNodes::EndNodeTitleBar ();
// pins and other node UI content omitted...
ImNodes::EndNode ();
Пользователь также должен отображать свои собственные связи между узлами. Ссылка — это кривая, соединяющая два атрибута. Ссылка — это всего лишь пара идентификаторов атрибутов. И, как узлы и атрибуты, ссылки также должны идентифицироваться уникальными целочисленными значениями:
std::vector<std::pair< int , int >> links;
// elsewhere in the code...
for ( int i = 0 ; i < links.size(); ++i)
{
const std::pair< int , int > p = links[i];
// in this case, we just use the array index of the link
// as the unique identifier
ImNodes::Link (i, p. first , p. second );
}
После вызова EndNodeEditor
вы можете проверить, была ли создана ссылка во время кадра, с помощью вызова функции IsLinkCreated
:
int start_attr, end_attr;
if (ImNodes::IsLinkCreated(&start_attr, &end_attr))
{
links. push_back ( std::make_pair (start_attr, end_attr));
}
Помимо проверки новых ссылок, вы также можете проверить, наводятся ли на элементы пользовательского интерфейса курсор мыши:
int node_id;
if (ImNodes::IsNodeHovered(&node_id))
{
node_hovered = node_id;
}
Вы также можете проверить, выбран ли какой-либо узел. Узлы можно щелкнуть или выбрать, щелкнув и перетащив на них селектор поля.
// Note that since many nodes can be selected at once, we first need to query the number of
// selected nodes before getting them.
const int num_selected_nodes = ImNodes::NumSelectedNodes();
if (num_selected_nodes > 0 )
{
std::vector< int > selected_nodes;
selected_nodes. resize (num_selected_nodes);
ImNodes::GetSelectedNodes (selected_nodes. data ());
}
См. imnodes.h
для получения дополнительных функций, связанных с событиями пользовательского интерфейса.
Как и dear imgui
, стиль пользовательского интерфейса можно изменить. Вы можете установить цветовой стиль отдельных узлов, выводов и ссылок в середине кадра, вызвав ImNodes::PushColorStyle
и ImNodes::PopColorStyle
.
// set the titlebar color of an individual node
ImNodes::PushColorStyle (
ImNodesCol_TitleBar, IM_COL32( 11 , 109 , 191 , 255 ));
ImNodes::PushColorStyle (
ImNodesCol_TitleBarSelected, IM_COL32( 81 , 148 , 204 , 255 ));
ImNodes::BeginNode (hardcoded_node_id);
// node internals here...
ImNodes::EndNode ();
ImNodes::PopColorStyle ();
ImNodes::PopColorStyle ();
Если стиль не устанавливается в середине кадра, вместо этого можно вызвать ImNodes::GetStyle
, и значения можно установить непосредственно в массив стилей.
// set the titlebar color for all nodes
ImNodesStyle& style = ImNodes::GetStyle();
style.colors[ImNodesCol_TitleBar] = IM_COL32( 232 , 27 , 86 , 255 );
style.colors[ImNodesCol_TitleBarSelected] = IM_COL32( 241 , 108 , 146 , 255 );
Для более быстрой навигации по большим графикам вы можете использовать наложение интерактивной мини-карты. Мини-карту можно масштабировать и прокручивать. Узлы редактора будут соответствующим образом отслеживать панорамирование мини-карты.
ImGui::Begin ( " node editor " );
ImNodes::BeginNodeEditor ();
// add nodes...
// must be called right before EndNodeEditor
ImNodes::MiniMap ();
ImNodes::EndNodeEditor ();
ImGui::End ();
Относительный размер и угловое расположение мини-карты в пространстве редактора можно указать следующим образом:
// MiniMap is a square region with a side length that is 20% the largest editor canvas dimension
// See ImNodesMiniMapLocation_ for other corner locations
ImNodes::MiniMap ( 0 . 2f , ImNodesMiniMapLocation_TopRight);
Мини-карта также поддерживает ограниченную настройку наведения узла с помощью определяемого пользователем обратного вызова.
// User callback
void mini_map_node_hovering_callback ( int node_id, void * user_data)
{
ImGui::SetTooltip ( " This is node %d " , node_id);
}
// Later on...
ImNodes::MiniMap ( 0 . 2f , ImNodesMiniMapLocation_TopRight, mini_map_node_hovering_callback, custom_user_data);
// 'custom_user_data' can be used to supply extra information needed for drawing within the callback
ImNodes можно настроить, предоставив заголовок imnodes_config.h
и указав определение IMNODES_USER_CONFIG=imnodes_config.h
при компиляции.
В настоящее время можно переопределить тип функции обратного вызова при наведении на мини-карту. Это полезно при создании привязок для другого языка.
Вот пример imnodes_config.h, который генерирует оболочку pybind для обратного вызова.
# pragma once
# include < pybind11/functional.h >
namespace pybind11 {
inline bool PyWrapper_Check (PyObject *o) { return true ; }
class wrapper : public object {
public:
PYBIND11_OBJECT_DEFAULT (wrapper, object, PyWrapper_Check)
wrapper ( void * x) { m_ptr = (PyObject*)x; }
explicit operator bool () const { return m_ptr != nullptr && m_ptr != Py_None; }
};
} // namespace pybind11
namespace py = pybind11;
# define ImNodesMiniMapNodeHoveringCallback py::wrapper
# define ImNodesMiniMapNodeHoveringCallbackUserData py::wrapper
ImGui::Separator()
охватывает текущий диапазон окна. В результате использование разделителя внутри узла приведет к тому, что разделитель выйдет за пределы узла в сетку редактора узлов. См. каталог examples/
чтобы более подробно ознакомиться с использованием библиотеки.