Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,4 @@ if(IMNODES_EXAMPLES)
else()
target_link_libraries(hello X11 Xext GL)
endif()
endif()
endif()
250 changes: 201 additions & 49 deletions imnodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,11 +566,7 @@ ImVec2 GetScreenSpacePinCoordinates(const ImNodesEditorContext& editor, const Im

bool MouseInCanvas()
{
// This flag should be true either when hovering or clicking something in the canvas.
const bool is_window_hovered_or_focused = ImGui::IsWindowHovered() || ImGui::IsWindowFocused();

return is_window_hovered_or_focused &&
GImNodes->CanvasRectScreenSpace.Contains(ImGui::GetMousePos());
return GImNodes->IsHovered;
}

void BeginNodeSelection(ImNodesEditorContext& editor, const int node_idx)
Expand Down Expand Up @@ -1053,7 +1049,7 @@ void ClickInteractionUpdate(ImNodesEditorContext& editor)
cubic_bezier.P2,
cubic_bezier.P3,
GImNodes->Style.Colors[ImNodesCol_Link],
GImNodes->Style.LinkThickness,
GImNodes->Style.LinkThickness / editor.ZoomScale,
cubic_bezier.NumSegments);

const bool link_creation_on_snap =
Expand Down Expand Up @@ -1352,6 +1348,44 @@ void DrawGrid(ImNodesEditorContext& editor, const ImVec2& canvas_size)
}
}

inline void AppendDrawData(ImDrawList* src, ImVec2 origin, float scale)
{
ImDrawList* dl = ImGui::GetWindowDrawList();
const int vtx_start = dl->VtxBuffer.size();
const int idx_start = dl->IdxBuffer.size();
dl->VtxBuffer.resize(dl->VtxBuffer.size() + src->VtxBuffer.size());
dl->IdxBuffer.resize(dl->IdxBuffer.size() + src->IdxBuffer.size());
dl->CmdBuffer.reserve(dl->CmdBuffer.size() + src->CmdBuffer.size());
dl->_VtxWritePtr = dl->VtxBuffer.Data + vtx_start;
dl->_IdxWritePtr = dl->IdxBuffer.Data + idx_start;
const ImDrawVert* vtx_read = src->VtxBuffer.Data;
const ImDrawIdx* idx_read = src->IdxBuffer.Data;
for (int i = 0, c = src->VtxBuffer.size(); i < c; ++i)
{
dl->_VtxWritePtr[i].uv = vtx_read[i].uv;
dl->_VtxWritePtr[i].col = vtx_read[i].col;
dl->_VtxWritePtr[i].pos = vtx_read[i].pos * scale + origin;
}
for (int i = 0, c = src->IdxBuffer.size(); i < c; ++i)
{
dl->_IdxWritePtr[i] = idx_read[i] + (ImDrawIdx)vtx_start;
}
for (int i = 0, c = src->CmdBuffer.size(); i < c; ++i)
{
ImDrawCmd cmd = src->CmdBuffer[i];
cmd.IdxOffset += idx_start;
cmd.ClipRect.x = cmd.ClipRect.x * scale + origin.x;
cmd.ClipRect.y = cmd.ClipRect.y * scale + origin.y;
cmd.ClipRect.z = cmd.ClipRect.z * scale + origin.x;
cmd.ClipRect.w = cmd.ClipRect.w * scale + origin.y;
dl->CmdBuffer.push_back(cmd);
}

dl->_VtxCurrentIdx += src->VtxBuffer.size();
dl->_VtxWritePtr = dl->VtxBuffer.Data + dl->VtxBuffer.size();
dl->_IdxWritePtr = dl->IdxBuffer.Data + dl->IdxBuffer.size();
}

struct QuadOffsets
{
ImVec2 TopLeft, BottomLeft, BottomRight, TopRight;
Expand Down Expand Up @@ -1627,7 +1661,7 @@ void DrawLink(ImNodesEditorContext& editor, const int link_idx)
cubic_bezier.P2,
cubic_bezier.P3,
link_color,
GImNodes->Style.LinkThickness,
GImNodes->Style.LinkThickness / editor.ZoomScale,
cubic_bezier.NumSegments);
}

Expand Down Expand Up @@ -1684,6 +1718,11 @@ void EndPinAttribute()

void Initialize(ImNodesContext* context)
{
context->NodeEditorImgCtx = ImGui::CreateContext(ImGui::GetIO().Fonts);
context->NodeEditorImgCtx->IO.IniFilename = nullptr;
context->OriginalImgCtx = nullptr;

context->CanvasOriginalOrigin = ImVec2(0.0f, 0.0f);
context->CanvasOriginScreenSpace = ImVec2(0.0f, 0.0f);
context->CanvasRectScreenSpace = ImRect(ImVec2(0.f, 0.f), ImVec2(0.f, 0.f));
context->CurrentScope = ImNodesScope_None;
Expand All @@ -1700,7 +1739,11 @@ void Initialize(ImNodesContext* context)
StyleColorsDark(&context->Style);
}

void Shutdown(ImNodesContext* ctx) { EditorContextFree(ctx->DefaultEditorCtx); }
void Shutdown(ImNodesContext* ctx)
{
EditorContextFree(ctx->DefaultEditorCtx);
ImGui::DestroyContext(ctx->NodeEditorImgCtx);
}

// [SECTION] minimap

Expand All @@ -1721,8 +1764,8 @@ static inline bool IsMiniMapHovered()
static inline void CalcMiniMapLayout()
{
ImNodesEditorContext& editor = EditorContextGet();
const ImVec2 offset = GImNodes->Style.MiniMapOffset;
const ImVec2 border = GImNodes->Style.MiniMapPadding;
const ImVec2 offset = GImNodes->Style.MiniMapOffset / editor.ZoomScale;
const ImVec2 border = GImNodes->Style.MiniMapPadding / editor.ZoomScale;
const ImRect editor_rect = GImNodes->CanvasRectScreenSpace;

// Compute the size of the mini-map area
Expand Down Expand Up @@ -1818,7 +1861,7 @@ static void MiniMapDrawNode(ImNodesEditorContext& editor, const int node_idx)
node_rect.Min, node_rect.Max, mini_map_node_background, mini_map_node_rounding);

GImNodes->CanvasDrawList->AddRect(
node_rect.Min, node_rect.Max, mini_map_node_outline, mini_map_node_rounding);
node_rect.Min, node_rect.Max, mini_map_node_outline, mini_map_node_rounding, 0, 1 / editor.ZoomScale);
}

static void MiniMapDrawLink(ImNodesEditorContext& editor, const int link_idx)
Expand Down Expand Up @@ -1858,7 +1901,7 @@ static void MiniMapDrawLink(ImNodesEditorContext& editor, const int link_idx)
cubic_bezier.P2,
cubic_bezier.P3,
link_color,
GImNodes->Style.LinkThickness * editor.MiniMapScaling,
GImNodes->Style.LinkThickness * editor.MiniMapScaling / editor.ZoomScale,
cubic_bezier.NumSegments);
}

Expand Down Expand Up @@ -1889,7 +1932,12 @@ static void MiniMapUpdate()
mini_map_rect.Min, mini_map_rect.Max, mini_map_background);

GImNodes->CanvasDrawList->AddRect(
mini_map_rect.Min, mini_map_rect.Max, GImNodes->Style.Colors[ImNodesCol_MiniMapOutline]);
mini_map_rect.Min,
mini_map_rect.Max,
GImNodes->Style.Colors[ImNodesCol_MiniMapOutline],
0,
0,
1 / editor.ZoomScale);

// Clip draw list items to mini-map rect (after drawing background/outline)
GImNodes->CanvasDrawList->PushClipRect(
Expand Down Expand Up @@ -1919,7 +1967,8 @@ static void MiniMapUpdate()
const ImRect rect = ScreenSpaceToMiniMapSpace(editor, GImNodes->CanvasRectScreenSpace);

GImNodes->CanvasDrawList->AddRectFilled(rect.Min, rect.Max, canvas_color);
GImNodes->CanvasDrawList->AddRect(rect.Min, rect.Max, outline_color);
GImNodes->CanvasDrawList->AddRect(
rect.Min, rect.Max, outline_color, 0, 0, 1 / editor.ZoomScale);
}

// Have to pop mini-map clip rect
Expand Down Expand Up @@ -2062,6 +2111,8 @@ void EditorContextMoveToNode(const int node_id)
editor.Panning.y = -node.Origin.y;
}

ImGuiContext* GetNodeEditorImGuiContext() { return GImNodes->NodeEditorImgCtx; }

void SetImGuiContext(ImGuiContext* ctx) { ImGui::SetCurrentContext(ctx); }

ImNodesIO& GetIO() { return GImNodes->Io; }
Expand Down Expand Up @@ -2224,37 +2275,73 @@ void BeginNodeEditor()

GImNodes->ImNodesUIState = ImNodesUIState_None;

GImNodes->MousePos = ImGui::GetIO().MousePos;
GImNodes->LeftMouseClicked = ImGui::IsMouseClicked(0);
GImNodes->LeftMouseReleased = ImGui::IsMouseReleased(0);
GImNodes->LeftMouseDragging = ImGui::IsMouseDragging(0, 0.0f);
GImNodes->AltMouseClicked =
(GImNodes->Io.EmulateThreeButtonMouse.Modifier != NULL &&
*GImNodes->Io.EmulateThreeButtonMouse.Modifier && GImNodes->LeftMouseClicked) ||
ImGui::IsMouseClicked(GImNodes->Io.AltMouseButton);
GImNodes->AltMouseDragging =
(GImNodes->Io.EmulateThreeButtonMouse.Modifier != NULL && GImNodes->LeftMouseDragging &&
(*GImNodes->Io.EmulateThreeButtonMouse.Modifier)) ||
ImGui::IsMouseDragging(GImNodes->Io.AltMouseButton, 0.0f);
GImNodes->AltMouseScrollDelta = ImGui::GetIO().MouseWheel;
GImNodes->MultipleSelectModifier =
(GImNodes->Io.MultipleSelectModifier.Modifier != NULL
? *GImNodes->Io.MultipleSelectModifier.Modifier
: ImGui::GetIO().KeyCtrl);

GImNodes->ActiveAttribute = false;
GImNodes->IsHovered = false;

ImGui::BeginGroup();
{
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(1.f, 1.f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.f, 0.f));
ImGui::PushStyleColor(ImGuiCol_ChildBg, GImNodes->Style.Colors[ImNodesCol_GridBackground]);
ImGui::BeginChild(
"scrolling_region",
ImVec2(0.f, 0.f),
true,
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoScrollWithMouse);
// Setup zoom context
ImVec2 canvas_size = ImGui::GetContentRegionAvail();
GImNodes->CanvasOriginalOrigin = ImGui::GetCursorScreenPos();
GImNodes->OriginalImgCtx = ImGui::GetCurrentContext();

// Copy config settings in IO from main context, avoiding input fields
memcpy(
(void*)&GImNodes->NodeEditorImgCtx->IO,
(void*)&GImNodes->OriginalImgCtx->IO,
offsetof(ImGuiIO, SetPlatformImeDataFn) +
sizeof(GImNodes->OriginalImgCtx->IO.SetPlatformImeDataFn));

GImNodes->NodeEditorImgCtx->IO.BackendPlatformUserData = nullptr;
GImNodes->NodeEditorImgCtx->IO.BackendRendererUserData = nullptr;
GImNodes->NodeEditorImgCtx->IO.IniFilename = nullptr;
GImNodes->NodeEditorImgCtx->IO.ConfigInputTrickleEventQueue = false;
GImNodes->NodeEditorImgCtx->IO.DisplaySize = ImMax(canvas_size / editor.ZoomScale, ImVec2(0, 0));
GImNodes->NodeEditorImgCtx->Style = GImNodes->OriginalImgCtx->Style;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scaled(Zoom) context does not need viewport option, nor docking.
So, we have to disable (only if a user enabled these flags).
Otherwise assertion failur in ImGui::ErrorCheckNewFrameSanityChecks()

GImNodes->NodeEditorImgCtx->IO.ConfigFlags -= ImGuiConfigFlags_ViewportsEnable;
GImNodes->NodeEditorImgCtx->IO.ConfigFlags -= ImGuiConfigFlags_DockingEnable;

// Nav (tabbing) needs to be disabled otherwise it doubles up with the main context
// not sure how to get this working correctly
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoMove;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need ImGuiWindowFlags_NoBackground for docking feature


// Button to capture mouse events and hover test
ImGui::BeginChild("canvas_no_drag", canvas_size, 0, windowFlags);

if (ImGui::IsWindowHovered())
{
GImNodes->IsHovered = true;
}
else
{
windowFlags |= ImGuiWindowFlags_NoInputs;
GImNodes->NodeEditorImgCtx->IO.ConfigFlags |= ImGuiConfigFlags_NoMouse;
}

// Copy IO events
GImNodes->NodeEditorImgCtx->InputEventsQueue = GImNodes->OriginalImgCtx->InputEventsTrail;
for (ImGuiInputEvent& e : GImNodes->NodeEditorImgCtx->InputEventsQueue)
{
if (e.Type == ImGuiInputEventType_MousePos)
{
e.MousePos.PosX =
(e.MousePos.PosX - GImNodes->CanvasOriginalOrigin.x) / editor.ZoomScale;
e.MousePos.PosY =
(e.MousePos.PosY - GImNodes->CanvasOriginalOrigin.y) / editor.ZoomScale;
}
}

ImGui::SetCurrentContext(GImNodes->NodeEditorImgCtx);
ImGui::NewFrame();

ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(1, 1));
ImGui::PushStyleColor(ImGuiCol_WindowBg, GImNodes->Style.Colors[ImNodesCol_GridBackground]);
ImGui::Begin("editor_canvas", nullptr, windowFlags);
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();

GImNodes->CanvasOriginScreenSpace = ImGui::GetCursorScreenPos();

// NOTE: we have to fetch the canvas draw list *after* we call
Expand All @@ -2263,16 +2350,35 @@ void BeginNodeEditor()
DrawListSet(ImGui::GetWindowDrawList());

{
const ImVec2 canvas_size = ImGui::GetWindowSize();
const ImVec2 window_size = ImGui::GetWindowSize();
GImNodes->CanvasRectScreenSpace = ImRect(
EditorSpaceToScreenSpace(ImVec2(0.f, 0.f)), EditorSpaceToScreenSpace(canvas_size));
EditorSpaceToScreenSpace(ImVec2(0.f, 0.f)), EditorSpaceToScreenSpace(window_size));

if (GImNodes->Style.Flags & ImNodesStyleFlags_GridLines)
{
DrawGrid(editor, canvas_size);
DrawGrid(editor, window_size);
}
}
}

// Cache inputs
GImNodes->MousePos = ImGui::GetIO().MousePos;
GImNodes->LeftMouseClicked = ImGui::IsMouseClicked(0);
GImNodes->LeftMouseReleased = ImGui::IsMouseReleased(0);
GImNodes->LeftMouseDragging = ImGui::IsMouseDragging(0, 0.0f);
GImNodes->AltMouseClicked =
(GImNodes->Io.EmulateThreeButtonMouse.Modifier != NULL &&
*GImNodes->Io.EmulateThreeButtonMouse.Modifier && GImNodes->LeftMouseClicked) ||
ImGui::IsMouseClicked(GImNodes->Io.AltMouseButton);
GImNodes->AltMouseDragging =
(GImNodes->Io.EmulateThreeButtonMouse.Modifier != NULL && GImNodes->LeftMouseDragging &&
(*GImNodes->Io.EmulateThreeButtonMouse.Modifier)) ||
ImGui::IsMouseDragging(GImNodes->Io.AltMouseButton, 0.0f);
GImNodes->AltMouseScrollDelta = ImGui::GetIO().MouseWheel;
GImNodes->MultipleSelectModifier =
(GImNodes->Io.MultipleSelectModifier.Modifier != NULL
? *GImNodes->Io.MultipleSelectModifier.Modifier
: ImGui::GetIO().KeyCtrl);
}

void EndNodeEditor()
Expand Down Expand Up @@ -2417,12 +2523,30 @@ void EndNodeEditor()
// Finally, merge the draw channels
GImNodes->CanvasDrawList->ChannelsMerge();

// pop style
ImGui::EndChild(); // end scrolling region
ImGui::PopStyleColor(); // pop child window background color
ImGui::PopStyleVar(); // pop window padding
ImGui::PopStyleVar(); // pop frame padding
GImNodes->OriginalImgCtx->WantTextInputNextFrame = ImMax(
GImNodes->OriginalImgCtx->WantTextInputNextFrame,
GImNodes->NodeEditorImgCtx->WantTextInputNextFrame);

if (MouseInCanvas())
{
GImNodes->OriginalImgCtx->MouseCursor = GImNodes->NodeEditorImgCtx->MouseCursor;
}

// End frame for zoom context
ImGui::End();
ImGui::Render();

ImDrawData* draw_data = ImGui::GetDrawData();

ImGui::SetCurrentContext(GImNodes->OriginalImgCtx);
GImNodes->OriginalImgCtx = nullptr;

ImGui::EndChild();
ImGui::EndGroup();

// Copy draw data over to original context
for (int i = 0; i < draw_data->CmdListsCount; ++i)
AppendDrawData(draw_data->CmdLists[i], GImNodes->CanvasOriginalOrigin, editor.ZoomScale);
}

void MiniMap(
Expand Down Expand Up @@ -2796,6 +2920,34 @@ void SnapNodeToGrid(int node_id)
node.Origin = SnapOriginToGrid(node.Origin);
}

float EditorContextGetZoom() { return EditorContextGet().ZoomScale; }

void EditorContextSetZoom(float zoom_scale, ImVec2 zoom_centering_pos)
{
IM_ASSERT(GImNodes->CurrentScope == ImNodesScope_None);

ImNodesEditorContext& editor = EditorContextGet();
const float new_zoom = ImMax(0.1f, ImMin(10.0f, zoom_scale));

zoom_centering_pos -= GImNodes->CanvasOriginalOrigin;
editor.Panning += zoom_centering_pos / new_zoom - zoom_centering_pos / editor.ZoomScale;

// Fix mouse position
GImNodes->NodeEditorImgCtx->IO.MousePos *= editor.ZoomScale / new_zoom;

editor.ZoomScale = new_zoom;
}

ImVec2 ConvertToEditorContextSpace(const ImVec2& screen_space_pos)
{
return (screen_space_pos - GImNodes->CanvasOriginalOrigin) / EditorContextGet().ZoomScale;
}

ImVec2 ConvertFromEditorContextSpace(const ImVec2& screen_space_pos)
{
return (screen_space_pos * EditorContextGet().ZoomScale) + GImNodes->CanvasOriginalOrigin;
}

bool IsEditorHovered() { return MouseInCanvas(); }

bool IsNodeHovered(int* const node_id)
Expand Down
Loading
Loading