// Chatter — UI only. The white "page" plus the top control bar. Ink is drawn // directly into the framebuffer by the C++ InkEngine, not by the scene graph, // so there is no canvas item here. Keep the scene STATIC (only the buttons ever // repaint, on tap) so it never recomposites over the direct-drawn ink. import QtQuick import QtQuick.Window Window { id: root width: Screen.width height: Screen.height visible: true color: "white" // Finger gestures (the stylus is handled in C++ and is NOT delivered here): // 1 finger = erase wipe (a tap erases nothing — only movement does) // 2 fingers = vertical scroll (content follows the fingers) // The gesture type is latched until all fingers lift, so a 2-finger scroll // never turns into an erase when one finger is raised. Buttons (z:10) are above. MultiPointTouchArea { id: touch anchors.fill: parent z: 0 minimumTouchPoints: 1 maximumTouchPoints: 2 touchPoints: [ TouchPoint { id: tpA }, TouchPoint { id: tpB } ] property int mode: 0 // 0 idle, 1 erase, 2 scroll property real lastPanY: 0 function activeCount() { return (tpA.pressed ? 1 : 0) + (tpB.pressed ? 1 : 0); } function soloPoint() { return tpA.pressed ? tpA : tpB; } function handle() { var n = activeCount(); if (n >= 2) { var cy = (tpA.y + tpB.y) / 2; if (mode !== 2) { if (mode === 1) ink.eraseEnd(); mode = 2; lastPanY = cy; } else { ink.panBy(cy - lastPanY); lastPanY = cy; } } else if (n === 1) { if (mode === 0) { mode = 1; ink.eraseStart(soloPoint().x, soloPoint().y); } else if (mode === 1) { ink.eraseMove(soloPoint().x, soloPoint().y); } // mode === 2 (one finger left after a scroll): stay idle until all up } else { if (mode === 1) ink.eraseEnd(); else if (mode === 2) ink.panEnd(); // full refresh to clear scroll ghosts mode = 0; } } onPressed: handle() onUpdated: handle() onReleased: handle() } // Buttons keep a FIXED appearance in the Qt scene (rendered once); their // pressed/normal feedback and actions are handled by the ink engine // (ink.flashButton / ink.activateButton) so both finger AND stylus work and // updates are instant single-pass (no scene flashing). component TopButton: Rectangle { id: btn height: 90 // Size to the label so horizontal padding ≈ vertical padding. width: label.implicitWidth + (height - label.implicitHeight) color: "white" border.color: "black" border.width: 3 radius: 8 z: 10 property alias text: label.text Text { id: label; anchors.centerIn: parent; anchors.verticalCenterOffset: -2; font.pixelSize: 38 } // Register geometry once laid out (ink-exclusion, clear-redraw, stylus hit-test). Component.onCompleted: Qt.callLater(function() { ink.registerButton(btn.x, btn.y, btn.width, btn.height, btn.text); }) MouseArea { anchors.fill: parent onPressed: ink.flashButton(btn.x, btn.y, btn.width, btn.height, btn.text, true) onClicked: ink.activateButton(btn.text) } } TopButton { id: backBtn text: "Back" anchors.left: parent.left anchors.top: parent.top anchors.margins: 24 } TopButton { id: clearBtn text: "Clear" anchors.right: parent.right anchors.top: parent.top anchors.margins: 24 } }