-
Notifications
You must be signed in to change notification settings - Fork 57
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
Horizontal scrollbar doesn't function #5
Comments
actually the vertical scrollbar also doesn't function. It conflicts with canvas->offset. A fix would be to do something like:
But then scroll with the mouse fails which could be fixed with something like:
But this only works if the window is smaller than the canvas. So it bites in the tail again. Perhaps a scrollbar is useless anyway, I'm not sure. Perhaps I'll dive into it again when it's needed. |
You could also try putting canvas between |
That would be a nice workaround as well. I'll try that. Hope you don't mind me shooting in these little bugs. I'll get to pull requests once I find more time! Otherwise let me know how I can help. |
All of that is greatly appreciated :] This will give more battle-testing before i get around to using this code. I scrapped my initial idea for which i needed nodes so they werent put to use yet. |
@sphaero did |
From what I tested it doesn't:
|
Child should have no scrollbars. if you want them - add them to parent. But that is kind of pointless. Canvas is infinite itself. How can we add scrollbars to something infinite? |
still doesn't work. But the scrollbar should be determined on the rectangle of its contents. The min/max pos of current nodes? If I look at how it's done in Blender, it's indeed based on the contents of the canvas which makes sense to me. I'm going to read into the scrolling examples of ImGui |
Best working code for now is to set scroll value before calling BeginCanvas
However this does not work if you move nodes upward (above pos 0) |
From what I read so far I think the canvas needs a rectangle from which scroll position can be determined. But I have a feeling it could be simpler I just need more experience with ImGui. |
Another test. It seems its easiest to just draw the canvas inside a Begin/EndChild as you suggested. I don't think static ImNodes::CanvasState canvas{};
static ImVector<ImVec2> nodes{};
const ImGuiStyle& style = ImGui::GetStyle();
if (ImGui::Begin("ImNodes", nullptr, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar))
{
// child gives our own coordspace
ImGui::BeginChild("canvaschild", canvas.rect.GetSize() );
ImVec2 offset = ImVec2(); //offset is not needed I guess
ImDrawList* draw_list = ImGui::GetWindowDrawList();
const float grid = 64.0f;
ImVec2 pos = ImGui::GetWindowPos();
ImVec2 size = ImGui::GetWindowSize();
// For the test I added a rect to canvas to maintain the rectangle containing nodes
// canvas rect is in screen coords
// set the rect to window size if it's smaller
if (canvas.rect.GetWidth() < GetWindowWidth() )
{
canvas.rect.Min = pos;
canvas.rect.Max = pos + size;
}
ImU32 grid_color = ImColor(1.0f, 1.0f, 0.5f, 1.0f);//canvas->colors[ColCanvasLines]);
for (float x = fmodf(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(offset.y, grid); y < size.y;)
{
draw_list->AddLine(ImVec2(0, y) + pos, ImVec2(size.x, y) + pos, grid_color);
y += grid;
}
// Node 1 is just a button to add more nodes
ImGui::BeginGroup();
if ( ImGui::Button("Button1") ) {
// add extra node at random pos
ImGui::SetCursorPos(ImVec2(0,0));
ImVec2 pos = ImVec2( rand() % 1000 -200, rand() % 1000 );
nodes.push_back( pos );
// add to canvas rectangle
ImVec2 screenpos = pos + GetCursorScreenPos();
canvas.rect.Add( ImRect(screenpos, screenpos + ImVec2(200, 100) ) );
}
ImGui::EndGroup();
// draw the rectangle of the first node
ImGui::GetForegroundDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), ImColor(1.0f,0.f,0.f,1.0f) );
// draw extra nodes
for (int i=0; i < nodes.size(); i++)
{
ImGui::BeginGroup();
ImGui::PushID(i);
ImGui::SetCursorPos(nodes[i]);
ImGui::Button("button");
ImGui::PopID();
ImGui::EndGroup();
}
// draw canvas.rect
ImGui::GetForegroundDrawList()->AddRect(canvas.rect.Min, canvas.rect.Max, ImColor(1.0f,1.f,0.f,1.0f) );
ImGui::EndChild();
}
ImGui::End(); It needs extra code to correct the canvas when nodes have a negative positions. |
Note to self: Another approach would be to render a custom Scrollbar based on ImGui::Scrollbar |
You can call I'm not sure I understand/follow the rest of the thread enough to understand it. |
Hey Omar. Thanks for responding. Forget previous comments. Basically we have an infinite canvas defined by a Begin/EndCanvas. Line 213 in a62c20f
BeginCanvas draws the grid in the content area of the window and uses an offset to position the grid correctly. Lines 249 to 260 in a62c20f
The offset is controlled using the mouse: Lines 225 to 241 in a62c20f
Then nodes can be drawn using Begin/EndNode which can contain regular ImGui widgets. Line 375 in a62c20f
This works fine. However a user can move around the canvas using the right mouse but can end up with no visible Nodes and no clue where to go to. A scrollbar can give a visual clue of the whereabouts of nodes on the canvas. Hence the quest for adding a scrollbar to the canvas window. The scrollbar somewhat works but only to the right or down. See this gif for example: Using the scrollbar to move around the canvas doesn't work yet but I think that would be easier than getting the scrollbar to function in all directions. So this currently not using any Begin/EndChild. Wrapping it in a Child gives the same result. |
I have a working concept which correctly handles the scrollbars: void ShowDemoWindow(bool*)
{
// holds the recangle of the canvas
static ImRect canvas_rect = ImRect(0,0,0,0);
// Window for the canvas
if ( ImGui::Begin("Canvas", nullptr, ImGuiWindowFlags_HorizontalScrollbar ) )
{
const ImGuiWindow* w = ImGui::GetCurrentWindow();
ImGui::PushID("canvaswidget");
ImGui::ItemAdd(w->ContentsRegionRect, ImGui::GetID("canvas"));
ImGuiIO& io = ImGui::GetIO();
// use mouse to move around
if (!ImGui::IsMouseDown(0) && ImGui::IsWindowHovered())
{
if (ImGui::IsMouseDragging(1))
{
// 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;
// todo: decrease the canvas
else
{
ImVec2 s = w->Scroll - io.MouseDelta;
SetScrollX(s.x);
SetScrollY(s.y);
}
}
}
// draw grid in the visible area of the window
ImDrawList* draw_list = ImGui::GetWindowDrawList();
const float grid = 64.0f;
ImVec2 pos = w->ClipRect.Min;
ImVec2 size = w->ClipRect.GetSize();
ImVec2 canvas_offset = w->Scroll + canvas_rect.Min;
ImU32 grid_color = ImColor(0.5f,0.f, 1.0f, 1.0f);
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;)
{
draw_list->AddLine(ImVec2(0, y) + pos, ImVec2(size.x, y) + pos, grid_color);
y += grid;
}
// draw the position of canvas rectangle for feedback
ImGui::GetForegroundDrawList()->AddRect(w->ContentsRegionRect.Min, w->ContentsRegionRect.Min + canvas_rect.GetSize(), ImColor(1.0f,0.f,1.f,1.0f) );
ImGui::PopID();
// set the size of the canvas if it's smaller than the content region
ImVec2 csize = canvas_rect.GetSize();
ImVec2 wsize = w->ContentsRegionRect.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;
ImGui::ItemSize(canvas_rect.GetSize());
}
ImGui::End();
} I'm not sure if I'm using ImGui in the right way like this. I think I'm telling ImGui the size of the canvas using Any feedback on this approach? |
Sorry I haven't had time to dig into. |
Well, CursorMaxPos record the maxmum position that has ever been reach, so it makes sense there that a dummy call to SetCursorPos would set it.
|
Yes, I understand its working now. I corrected the canvas coordinates by a simple
before adding widgets. I've switched from using ContentsRegionRect to InnerClipRect. It works now. Still some small artefacts:
I've added these changes to a branch: master...sphaero:scrollbar. Still need to test zooming before I'll do a PR |
Changing the Window flags to:
The horizontal scrollbar doesn't appear to scroll.
The text was updated successfully, but these errors were encountered: