Demo: Improve "Custom Rendering"->"Canvas" demo with a grid, scrolling and context menu.

This commit is contained in:
omar 2020-08-03 19:13:42 +02:00
parent 4929a8e4a5
commit 912c45ab23
2 changed files with 81 additions and 40 deletions

View File

@ -68,6 +68,8 @@ Other Changes:
by modifying the 'style.CircleSegmentMaxError' value. [@ShironekoBen]
- ImDrawList: Fixed minor bug introduced in 1.75 where AddCircle() with 12 segments would generate
an extra vertex. (This bug was mistakenly marked as fixed in earlier 1.77 release). [@ShironekoBen]
- Demo: Improve "Custom Rendering"->"Canvas" demo with a grid, scrolling and context menu.
Also showcase using InvisibleButton() will multiple mouse buttons flags.
- Demo: Tweak "Child Windows" section.
- Style Editor: Added preview of circle auto-tessellation when editing the corresponding value.
- Backends: OpenGL3: Added support for glad2 loader. (#3330) [@moritz-h]

View File

@ -4859,13 +4859,13 @@ static void ShowExampleAppCustomRendering(bool* p_open)
// overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your
// types and ImVec2/ImVec4. Dear ImGui defines overloaded operators but they are internal to imgui.cpp and not
// exposed outside (to avoid messing with your types) In this example we are not using the maths operators!
ImDrawList* draw_list = ImGui::GetWindowDrawList();
if (ImGui::BeginTabBar("##TabBar"))
{
if (ImGui::BeginTabItem("Primitives"))
{
ImGui::PushItemWidth(-ImGui::GetFontSize() * 10);
ImDrawList* draw_list = ImGui::GetWindowDrawList();
// Draw gradients
// (note that those are currently exacerbating our sRGB/Linear issues)
@ -4949,56 +4949,95 @@ static void ShowExampleAppCustomRendering(bool* p_open)
if (ImGui::BeginTabItem("Canvas"))
{
static ImVector<ImVec2> points;
struct ItemLine { ImVec2 p0, p1; ItemLine(const ImVec2& _p0, const ImVec2& _p1) { p0 = _p0; p1 = _p1; } };
static ImVector<ItemLine> lines;
static ImVec2 scrolling(0.0f, 0.0f);
static bool show_grid = true;
static bool adding_line = false;
if (ImGui::Button("Clear")) points.clear();
if (points.Size >= 2) { ImGui::SameLine(); if (ImGui::Button("Undo")) { points.pop_back(); points.pop_back(); } }
ImGui::Text("Left-click and drag to add lines,\nRight-click to undo");
// Here we are using InvisibleButton() as a convenience to 1) advance the cursor and 2) allows us to use
// IsItemHovered(). But you can also draw directly and poll mouse/keyboard by yourself.
// You can manipulate the cursor using GetCursorPos() and SetCursorPos().
// If you only use the ImDrawList API, you can notify the owner window of its extends with SetCursorPos(max).
ImVec2 canvas_p = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates!
ImGui::Checkbox("Show grid", &show_grid);
ImGui::Text("Mouse Left: drag to add lines,\nMouse Right: drag to scroll, click for context menu.");
// Typically you would use a BeginChild()/EndChild() pair to benefit from a clipping region + own scrolling.
// Here we demonstrate that this can be replaced by simple offsetting + custom drawing + PushClipRect/PopClipRect() calls.
// To use a child window instead we could use, e.g:
// ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Disable padding
// ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255)); // Set a background color
// ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), true, ImGuiWindowFlags_NoMove);
// ImGui::PopStyleColor();
// ImGui::PopStyleVar();
// [...]
// ImGui::EndChild();
// Using InvisibleButton() as a convenience 1) it will advance the layout cursor and 2) allows us to use IsItemHovered()/IsItemActive()
ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates!
ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available
if (canvas_sz.x < 50.0f) canvas_sz.x = 50.0f;
if (canvas_sz.y < 50.0f) canvas_sz.y = 50.0f;
draw_list->AddRectFilledMultiColor(canvas_p, ImVec2(canvas_p.x + canvas_sz.x, canvas_p.y + canvas_sz.y), IM_COL32(50, 50, 50, 255), IM_COL32(50, 50, 60, 255), IM_COL32(60, 60, 70, 255), IM_COL32(50, 50, 60, 255));
draw_list->AddRect(canvas_p, ImVec2(canvas_p.x + canvas_sz.x, canvas_p.y + canvas_sz.y), IM_COL32(255, 255, 255, 255));
ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y);
bool adding_preview = false;
ImGui::InvisibleButton("canvas", canvas_sz);
ImVec2 mouse_pos_global = ImGui::GetIO().MousePos;
ImVec2 mouse_pos_canvas = ImVec2(mouse_pos_global.x - canvas_p.x, mouse_pos_global.y - canvas_p.y);
// Draw border and background color
ImGuiIO& io = ImGui::GetIO();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(50, 50, 50, 255));
draw_list->AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255));
// This will catch our interactions
ImGui::InvisibleButton("canvas", canvas_sz, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
const bool is_hovered = ImGui::IsItemHovered(); // Hovered
const bool is_active = ImGui::IsItemActive(); // Held
const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y); // Lock scrolled origin
const ImVec2 mouse_pos_in_canvas(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
// Add first and second point
if (is_hovered && !adding_line && ImGui::IsMouseClicked(ImGuiMouseButton_Left))
{
lines.push_back(ItemLine(mouse_pos_in_canvas, mouse_pos_in_canvas));
adding_line = true;
}
if (adding_line)
{
adding_preview = true;
points.push_back(mouse_pos_canvas);
if (!ImGui::IsMouseDown(0))
adding_line = adding_preview = false;
}
if (ImGui::IsItemHovered())
{
if (!adding_line && ImGui::IsMouseClicked(0))
{
points.push_back(mouse_pos_canvas);
adding_line = true;
}
if (ImGui::IsMouseClicked(1) && !points.empty())
{
adding_line = adding_preview = false;
points.pop_back();
points.pop_back();
}
lines.back().p1 = mouse_pos_in_canvas;
if (!ImGui::IsMouseDown(ImGuiMouseButton_Left))
adding_line = false;
}
// Draw all lines in the canvas (with a clipping rectangle so they don't stray out of it).
draw_list->PushClipRect(canvas_p, ImVec2(canvas_p.x + canvas_sz.x, canvas_p.y + canvas_sz.y), true);
for (int i = 0; i < points.Size - 1; i += 2)
draw_list->AddLine(ImVec2(canvas_p.x + points[i].x, canvas_p.y + points[i].y), ImVec2(canvas_p.x + points[i + 1].x, canvas_p.y + points[i + 1].y), IM_COL32(255, 255, 0, 255), 2.0f);
// Pan (using zero mouse threshold)
if (is_active && ImGui::IsMouseDragging(ImGuiMouseButton_Right, 0.0f))
{
scrolling.x += io.MouseDelta.x;
scrolling.y += io.MouseDelta.y;
}
// Context menu (under default mouse threshold)
// We intentionally use the same button to demonstrate using mouse drag threshold. Some may feel panning should rely on same threshold.
ImVec2 drag_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);
if (drag_delta.x == 0.0f && drag_delta.y == 0.0f)
ImGui::OpenPopupContextItem("context");
if (ImGui::BeginPopup("context"))
{
if (adding_line)
lines.pop_back();
adding_line = false;
if (ImGui::MenuItem("Remove one", NULL, false, lines.Size > 0)) { lines.pop_back(); }
if (ImGui::MenuItem("Remove all", NULL, false, lines.Size > 0)) { lines.clear(); }
ImGui::EndPopup();
}
// Draw grid + all lines in the canvas
draw_list->PushClipRect(canvas_p0, canvas_p1, true);
if (show_grid)
{
const float GRID_STEP = 64.0f;
for (float x = fmodf(scrolling.x, GRID_STEP); x < canvas_sz.x; x += GRID_STEP)
draw_list->AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y), ImVec2(canvas_p0.x + x, canvas_p1.y), IM_COL32(200, 200, 200, 40));
for (float y = fmodf(scrolling.y, GRID_STEP); y < canvas_sz.y; y += GRID_STEP)
draw_list->AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y), ImVec2(canvas_p1.x, canvas_p0.y + y), IM_COL32(200, 200, 200, 40));
}
for (int n = 0; n < lines.Size; n++)
draw_list->AddLine(ImVec2(origin.x + lines[n].p0.x, origin.y + lines[n].p0.y), ImVec2(origin.x + lines[n].p1.x, origin.y + lines[n].p1.y), IM_COL32(255, 255, 0, 255), 2.0f);
draw_list->PopClipRect();
if (adding_preview)
points.pop_back();
ImGui::EndTabItem();
}