Draws the canvas pane's content: the StageView image plus its drag/scroll/key input handling, sized to whatever rect the caller's current ImGui child occupies (ImGui::GetContentRegionAvail()). When link-views is on, the drag/scroll/key input fans out to every buffer's Stage so switching buffers shows them synchronized. Rendering and resize stay on the selected Stage (only it is displayed).
| 130 | // buffers shows them synchronized. Rendering and resize stay on the |
| 131 | // selected Stage (only it is displayed). |
| 132 | void draw_canvas_pane(oid::host::GlfwCanvas& canvas, |
| 133 | oid::host::StageView& view, |
| 134 | oid::Stage& sel, |
| 135 | const oid::host::UiState& ui, |
| 136 | oid::host::StageManager& stages, |
| 137 | const oid::host::BufferModel& model, |
| 138 | PaneRenderSize& pane_size) { |
| 139 | // Render the Stage at the canvas PANE's size, in framebuffer pixels, so |
| 140 | // the offscreen texture's aspect ratio matches the on-screen rect it is |
| 141 | // displayed in: ImGui::Image stretches the texture to fill the rect, so |
| 142 | // any texture:rect aspect mismatch visibly distorts the buffer. |
| 143 | const ImVec2 canvas_size = ImGui::GetContentRegionAvail(); |
| 144 | if (canvas_size.x < 1.0f || canvas_size.y < 1.0f) { |
| 145 | // Pane not laid out yet (e.g. the first frame, before the child sizes |
| 146 | // settle). Skip this frame rather than sizing to a degenerate rect. |
| 147 | return; |
| 148 | } |
| 149 | // One uniform DPI scale for BOTH axes (display pixels are square), so |
| 150 | // cw:ch == canvas_size aspect exactly. A per-axis DisplayFramebufferScale |
| 151 | // that is briefly non-uniform or unset at startup must never skew it. |
| 152 | float dpi = ImGui::GetIO().DisplayFramebufferScale.x; |
| 153 | if (dpi <= 0.0f) { |
| 154 | dpi = 1.0f; |
| 155 | } |
| 156 | const int cw = (std::max)(1, static_cast<int>(canvas_size.x * dpi + 0.5f)); |
| 157 | const int ch = (std::max)(1, static_cast<int>(canvas_size.y * dpi + 0.5f)); |
| 158 | // Logical pane size: the units the camera and mouse math operate in |
| 159 | // (Qt-native parity, see the PaneRenderSize comment above). |
| 160 | const int lw = (std::max)(1, static_cast<int>(canvas_size.x + 0.5f)); |
| 161 | const int lh = (std::max)(1, static_cast<int>(canvas_size.y + 0.5f)); |
| 162 | // Publish this frame's logical pane size for GlfwCanvas's SizeProvider |
| 163 | // before anything below reads canvas.render_width()/render_height() |
| 164 | // through it. |
| 165 | pane_size.width = lw; |
| 166 | pane_size.height = lh; |
| 167 | // Sync the render target (framebuffer px, for a crisp raster) and the |
| 168 | // camera projection (logical points, Qt units) to the current pane size |
| 169 | // every frame -- both are cheap self-guarded no-ops when unchanged. |
| 170 | // Doing it unconditionally (rather than only on a cached size change) |
| 171 | // means a first-frame or buffer-switch mismatch self-corrects on the next |
| 172 | // frame instead of sticking until the user resizes a pane. |
| 173 | view.ensure_size(cw, ch); |
| 174 | sel.resize_callback(lw, lh); |
| 175 | const GLuint tex = view.render(sel); |
| 176 | |
| 177 | ImGui::Image(static_cast<ImTextureID>(tex), |
| 178 | canvas_size, |
| 179 | ImVec2(0, 1), |
| 180 | ImVec2(1, 0)); // flip V (FBO origin is bottom-left) |
| 181 | |
| 182 | // ImGui mouse coordinates are already in screen (logical) points -- the |
| 183 | // same pane-logical frame the camera operates in (see PaneRenderSize), |
| 184 | // so positions/deltas below are fed 1:1, exactly like the native Qt |
| 185 | // canvas (its mouse scale factor render_width()/width() is 1). |
| 186 | const ImVec2 img_min = ImGui::GetItemRectMin(); |
| 187 | const ImVec2 img_size = ImGui::GetItemRectSize(); |
| 188 | |
| 189 | // Overlay an invisible button covering the image so a drag over the |
no test coverage detected