107 lines
3.8 KiB
QML
107 lines
3.8 KiB
QML
|
|
// 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
|
||
|
|
}
|
||
|
|
}
|