Uma extensão de editor de nó pequena e livre de dependências para o querido imgui.
Imnodes tem como objetivo fornecer uma interface simples e de modo imediato para criar um editor de nós dentro de uma janela ImGui. Imnodes fornece blocos de construção simples e personalizáveis que um usuário precisa para construir seu editor de nós.
Características:
imnodes.h
, imnodes_internal.h
e imnodes.cpp
em seu projeto junto com ImGui. Este repositório inclui alguns arquivos de exemplo, em example/
. Eles pretendem ser exemplos simples, dando uma ideia do que você pode construir com imnodes.
Se precisar criar os exemplos, você pode usar o script CMake fornecido para fazer isso.
# 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
Observe que isso não foi testado no Linux e provavelmente falhará na plataforma.
Aqui está uma pequena visão geral de como a extensão é usada. Para obter mais informações sobre exemplos de uso, vá até o final do README.
Antes que qualquer coisa possa ser feita, a biblioteca deve ser inicializada. Isso pode ser feito ao mesmo tempo que a inicialização dear imgui
.
ImGui::CreateContext ();
ImNodes::CreateContext ();
// elsewhere in the code...
ImNodes::DestroyContext ();
ImGui::DestroyContext ();
O editor de nós é um espaço de trabalho que contém nós. O editor de nós deve ser instanciado em uma janela, como qualquer outro elemento da UI.
ImGui::Begin ( " node editor " );
ImNodes::BeginNodeEditor ();
ImNodes::EndNodeEditor ();
ImGui::End ();
Agora você deve ter uma área de trabalho com uma grade visível na janela. Um nó vazio agora pode ser instanciado:
const int hardcoded_node_id = 1 ;
ImNodes::BeginNodeEditor ();
ImNodes::BeginNode (hardcoded_node_id);
ImGui::Dummy (ImVec2( 80 . 0f , 45 . 0f ));
ImNodes::EndNode ();
ImNodes::EndNodeEditor ();
Nós, como janelas no dear imgui
devem ser identificados exclusivamente. Mas não podemos usar os títulos dos nós para identificação, porque deveria ser possível ter muitos nós com o mesmo nome no espaço de trabalho. Em vez disso, você apenas usa números inteiros para identificação.
Atributos são o conteúdo da UI do nó. Um atributo terá um alfinete (o pequeno círculo) em cada lado do nó. Existem dois tipos de atributos: atributos de entrada e atributos de saída. Os pinos de atributos de entrada estão no lado esquerdo do nó e os pinos de atributos de saída estão à direita. Assim como os nós, os pinos devem ser identificados de forma exclusiva.
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 ();
A extensão realmente não se importa com o que está no atributo. Ele apenas renderiza o pino do atributo e permite ao usuário criar links entre os pinos.
Uma barra de título pode ser adicionada ao nó usando BeginNodeTitleBar
e EndNodeTitleBar
. Assim como os atributos, você coloca o conteúdo da barra de título entre as chamadas de função. Observe que essas funções devem ser chamadas antes de adicionar atributos ou outros elementos de interface do usuário dear imgui
ao nó, uma vez que o layout do nó é construído em ordem, de cima para baixo.
ImNodes::BeginNode (hardcoded_node_id);
ImNodes::BeginNodeTitleBar ();
ImGui::TextUnformatted ( " output node " );
ImNodes::EndNodeTitleBar ();
// pins and other node UI content omitted...
ImNodes::EndNode ();
O usuário também deve renderizar seus próprios links entre nós. Um link é uma curva que conecta dois atributos. Um link é apenas um par de IDs de atributos. E assim como os nós e atributos, os links também devem ser identificados por valores inteiros exclusivos:
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 );
}
Após EndNodeEditor
ter sido chamado, você pode verificar se um link foi criado durante o quadro com a chamada de função IsLinkCreated
:
int start_attr, end_attr;
if (ImNodes::IsLinkCreated(&start_attr, &end_attr))
{
links. push_back ( std::make_pair (start_attr, end_attr));
}
Além de verificar se há novos links, você também pode verificar se os elementos da UI estão sendo passados pelo cursor do mouse:
int node_id;
if (ImNodes::IsNodeHovered(&node_id))
{
node_hovered = node_id;
}
Você também pode verificar se algum nó foi selecionado. Os nós podem ser clicados ou selecionados clicando e arrastando o seletor de caixa sobre eles.
// 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 ());
}
Consulte imnodes.h
para obter mais funções relacionadas a eventos de UI.
Assim como dear imgui
, o estilo da IU pode ser alterado. Você pode definir o estilo de cor de nós, pinos e links individuais no meio do quadro chamando ImNodes::PushColorStyle
e 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 ();
Se o estilo não estiver sendo definido no meio do quadro, ImNodes::GetStyle
poderá ser chamado e os valores poderão ser definidos diretamente na matriz de estilo.
// 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 );
Para lidar com uma navegação mais rápida em gráficos grandes, você pode usar uma sobreposição de minimapa interativo. O minimapa pode ser ampliado e rolado. Os nós editores rastrearão a panorâmica do minimapa de acordo.
ImGui::Begin ( " node editor " );
ImNodes::BeginNodeEditor ();
// add nodes...
// must be called right before EndNodeEditor
ImNodes::MiniMap ();
ImNodes::EndNodeEditor ();
ImGui::End ();
O tamanho relativo e a localização do canto do minimapa no espaço do editor podem ser especificados da seguinte forma:
// 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);
O minimapa também suporta personalização limitada de flutuação de nó por meio de um retorno de chamada definido pelo usuário.
// 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 pode ser personalizado fornecendo um cabeçalho imnodes_config.h
e especificando a definição de IMNODES_USER_CONFIG=imnodes_config.h
durante a compilação.
Atualmente é possível substituir o tipo da função de retorno de chamada flutuante do minimapa. Isto é útil ao gerar ligações para outro idioma.
Aqui está um exemplo imnodes_config.h, que gera um wrapper pybind para o retorno de chamada.
# 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()
abrange a extensão da janela atual. Como resultado, usar um separador dentro de um nó fará com que o separador saia do nó para a grade do editor de nós. Veja o diretório examples/
para ver o uso da biblioteca com mais detalhes.