Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scrollbars to the canvas #11

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
58 changes: 47 additions & 11 deletions ImNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,38 +222,56 @@ void BeginCanvas(CanvasState* canvas)
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImGuiIO& io = ImGui::GetIO();

// initialize contents_rect to the current window viewport
canvas->contents_rect = ImRect(canvas->rect.Min + w->Scroll, canvas->rect.Min + w->Scroll + w->InnerClipRect.GetSize() );//ImRect(0,0,0,0);

if (!ImGui::IsMouseDown(0) && ImGui::IsWindowHovered())
{
if (ImGui::IsMouseDragging(1))
canvas->offset += io.MouseDelta;
{
// handle edges of canvas and increase it
if (w->Scroll.x == 0.f && io.MouseDelta.x > 0.f )
canvas->rect.Min.x -= io.MouseDelta.x;
if (w->Scroll.y == 0.f && io.MouseDelta.y > 0.f )
canvas->rect.Min.y -= io.MouseDelta.y;
if (w->Scroll.x == w->ScrollMax.x && io.MouseDelta.x < 0.f )
canvas->rect.Max.x -= io.MouseDelta.x;
if ( w->Scroll.y == w->ScrollMax.y && io.MouseDelta.y < 0.)
canvas->rect.Max.y -= io.MouseDelta.y;

ImVec2 s = w->Scroll - io.MouseDelta;
ImGui::SetScrollX(s.x);
ImGui::SetScrollY(s.y);
canvas->contents_rect.Translate(io.MouseDelta*-1);
}

if (io.KeyShift && !io.KeyCtrl)
canvas->offset.x += io.MouseWheel * 16.0f;
ImGui::SetScrollX( ImGui::GetScrollX() + io.MouseWheel * -16.0f);

if (!io.KeyShift && !io.KeyCtrl)
canvas->offset.y += io.MouseWheel * 16.0f;
if (!io.KeyShift && !io.KeyCtrl && io.MouseWheel != 0.f )
ImGui::SetScrollY( ImGui::GetScrollY() + io.MouseWheel * -16.0f);

if (!io.KeyShift && io.KeyCtrl)
{
if (io.MouseWheel != 0)
canvas->zoom = ImClamp(canvas->zoom + io.MouseWheel * canvas->zoom / 16.f, 0.3f, 3.f);
canvas->offset += ImGui::GetMouseDragDelta();
}
}

const float grid = 64.0f * canvas->zoom;

ImVec2 pos = ImGui::GetWindowPos();
ImVec2 size = ImGui::GetWindowSize();
ImVec2 pos = w->ClipRect.Min;
ImVec2 size = w->ClipRect.GetSize();
ImVec2 canvas_offset = canvas->contents_rect.Min; // same as viewport's min

ImU32 grid_color = ImColor(canvas->colors[ColCanvasLines]);
for (float x = fmodf(canvas->offset.x, grid); x < size.x;)
for (float x = fmodf(-canvas_offset.x, grid); x < size.x;)
{
draw_list->AddLine(ImVec2(x, 0) + pos, ImVec2(x, size.y) + pos, grid_color);
x += grid;
}

for (float y = fmodf(canvas->offset.y, grid); y < size.y;)
for (float y = fmodf(-canvas_offset.y, grid); y < size.y;)
{
draw_list->AddLine(ImVec2(0, y) + pos, ImVec2(size.x, y) + pos, grid_color);
y += grid;
Expand All @@ -267,10 +285,23 @@ void EndCanvas()
assert(gCanvas != nullptr); // Did you forget calling BeginCanvas()?

ImDrawList* draw_list = ImGui::GetWindowDrawList();
const ImGuiWindow* w = ImGui::GetCurrentWindow();
auto* canvas = gCanvas;
auto* impl = canvas->_impl;
const ImGuiStyle& style = ImGui::GetStyle();

// set the size of the canvas if it's smaller than the content region
ImVec2 csize = canvas->rect.GetSize();
ImVec2 wsize = w->InnerClipRect.GetSize();
if ( csize.x < wsize.x )
canvas->rect.Max.x = canvas->rect.Min.x + wsize.x;
if ( csize.y < wsize.y )
canvas->rect.Max.y = canvas->rect.Min.y + wsize.y;
// set the cursor to the maximum canvas position so imgui knows where to put scrollbars
ImGui::SetCursorPos(canvas->contents_rect.GetSize());
// switch canvas->rect to the contents_rect to enable shrinking it
canvas->rect = canvas->contents_rect;

// Draw pending connection
if (const ImGuiPayload* payload = ImGui::GetDragDropPayload())
{
Expand Down Expand Up @@ -379,6 +410,7 @@ bool BeginNode(void* node_id, ImVec2* pos, bool* selected)
assert(pos != nullptr);
assert(selected != nullptr);
const ImGuiStyle& style = ImGui::GetStyle();
const ImGuiWindow* w = ImGui::GetCurrentWindow();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
auto* canvas = gCanvas;
auto* impl = canvas->_impl;
Expand All @@ -399,7 +431,7 @@ bool BeginNode(void* node_id, ImVec2* pos, bool* selected)
else
{
// Top-let corner of the node
ImGui::SetCursorScreenPos(ImGui::GetWindowPos() + (*pos) * canvas->zoom + canvas->offset);
ImGui::SetCursorScreenPos(w->InnerClipRect.Min - w->Scroll + (*pos) * canvas->zoom - canvas->rect.Min);
}

ImGui::PushID(node_id);
Expand Down Expand Up @@ -429,6 +461,10 @@ void EndNode()
ImGui::GetItemRectMax() + style.ItemInnerSpacing * canvas->zoom
};

// add the node rect to the canvas content_rect, used to determine occupied canvas area
canvas->contents_rect.Min = ImMin(canvas->contents_rect.Min, node_pos);
canvas->contents_rect.Max = ImMax(canvas->contents_rect.Max, node_pos + node_rect.GetSize());

// Render frame
draw_list->ChannelsSetCurrent(0);

Expand Down Expand Up @@ -494,7 +530,7 @@ void EndNode()
{
// Upon node creation we would like it to be positioned at the center of mouse cursor. This can be done only
// once widget dimensions are known at the end of rendering and thus on the next frame.
node_pos = ImGui::GetMousePos() - ImGui::GetCurrentWindow()->Pos - canvas->offset - (node_rect.GetSize() / 2);
node_pos = ImGui::GetMousePos() - ImGui::GetCurrentWindow()->InnerClipRect.Min + ImGui::GetCurrentWindow()->Scroll + canvas->rect.Min - (node_rect.GetSize() / 2);
impl->auto_position_node_id = nullptr;
}
break;
Expand Down
6 changes: 4 additions & 2 deletions ImNodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@ struct IMGUI_API CanvasState
{
/// Current zoom of canvas.
float zoom = 1.0;
/// Current scroll offset of canvas.
ImVec2 offset{};
/// Contents rectangle of canvas
ImRect rect = { 0.f, 0.f, 0.f, 0.f };
/// Occupied contents rectangle of canvas
ImRect contents_rect {};
/// Colors used to style elements of this canvas.
ImColor colors[StyleColor::ColMax];
/// Style parameters
Expand Down
2 changes: 1 addition & 1 deletion sample.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ void ShowDemoWindow(bool*)
static ImNodes::CanvasState canvas{};

const ImGuiStyle& style = ImGui::GetStyle();
if (ImGui::Begin("ImNodes", nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse))
if (ImGui::Begin("ImNodes", nullptr, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoScrollWithMouse))
{
// We probably need to keep some state, like positions of nodes/slots for rendering connections.
ImNodes::BeginCanvas(&canvas);
Expand Down