pragma ComponentBehavior: Bound import QtQuick import ZShell.Internal Item { id: root property color inkColor: "#ff3b30" property real inkWidth: 4 property string tool: "none" signal changed function clearAll() { committedCanvas.clear(); previewCanvas.clear(); changed(); } function sampleShape(tool, x1, y1, x2, y2) { const pts = []; switch (tool) { case "line": pts.push([x1, y1], [x2, y2]); break; case "arrow": { const angle = Math.atan2(y2 - y1, x2 - x1); const headLen = 14 + root.inkWidth * 2; pts.push([x1, y1], [x2, y2]); pts.push([x2 - headLen * Math.cos(angle - Math.PI / 6), y2 - headLen * Math.sin(angle - Math.PI / 6)]); pts.push([x2, y2]); pts.push([x2 - headLen * Math.cos(angle + Math.PI / 6), y2 - headLen * Math.sin(angle + Math.PI / 6)]); break; } case "rect": pts.push([x1, y1], [x2, y1], [x2, y2], [x1, y2], [x1, y1]); break; case "ellipse": { const cx = (x1 + x2) / 2, cy = (y1 + y2) / 2; const rx = Math.abs(x2 - x1) / 2, ry = Math.abs(y2 - y1) / 2; const perimeter = Math.PI * (3 * (rx + ry) - Math.sqrt((3 * rx + ry) * (rx + 3 * ry))); const steps = Math.max(48, Math.min(256, Math.ceil(perimeter / 4))); for (let i = 0; i <= steps; i++) { const a = (i / steps) * Math.PI * 2; pts.push([cx + rx * Math.cos(a), cy + ry * Math.sin(a)]); } break; } } return pts; } function undo() { committedCanvas.undoLastGroup(); changed(); } anchors.fill: parent StrokeCanvas { id: committedCanvas anchors.fill: parent penColor: root.inkColor penWidth: root.inkWidth } StrokeCanvas { id: previewCanvas anchors.fill: parent penColor: root.inkColor penWidth: root.inkWidth } PointHandler { id: drawHandler property real startX property real startY property bool started: false acceptedButtons: Qt.LeftButton | Qt.RightButton enabled: root.tool !== "none" onActiveChanged: { if (active) return; if (root.tool === "pen") { committedCanvas.endStroke(); } else if (started) { const pts = root.sampleShape(root.tool, startX, startY, point.position.x, point.position.y); if (pts.length > 0) { committedCanvas.beginStroke(pts[0][0], pts[0][1], true); for (let i = 1; i < pts.length; i++) committedCanvas.appendPoint(pts[i][0], pts[i][1]); committedCanvas.endStroke(); } previewCanvas.clear(); } started = false; root.changed(); } onPointChanged: { if (!active) return; if (point.pressedButtons & Qt.RightButton) { root.clearAll(); return; } const x = point.position.x; const y = point.position.y; if (root.tool === "pen") { if (!started) { started = true; committedCanvas.beginStroke(x, y); return; } committedCanvas.appendPoint(x, y); return; } if (!started) { started = true; startX = point.pressPosition.x; startY = point.pressPosition.y; } const pts = root.sampleShape(root.tool, startX, startY, x, y); if (pts.length > 0) { previewCanvas.beginStroke(pts[0][0], pts[0][1], true); for (let i = 1; i < pts.length; i++) previewCanvas.appendPoint(pts[i][0], pts[i][1]); } } } }