Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

[OT] QML Canvas Context2D and mouse input

by afoken (Chancellor)
on Jan 23, 2023 at 16:31 UTC ( [id://11149792]=perlquestion: print w/replies, xml ) Need Help??

afoken has asked for the wisdom of the Perl Monks concerning the following question:

So, I'm once again stuck with a legacy version of QML from Qt 5, Qt 5.9.6 to be exact.

I need to draw a kind of interactive "map" on a touch screen. The map has tens of irregularly shaped areas that ...

  • need to be colored individually depending on user input
  • need to respond individually to touch events (appearing as mouse clicks on the target device)
  • need to be labeled individually depending on user input

The map comes as an SVG file, but I will probably convert it to Javascript code painting on a Canvas object. That solves at least my coloring and labeling problems and leaves only the mouse input problem. Each area needs its own event handler, and clicking on the map background needs to be ignored.

I can only put a single MouseArea into the Canvas, and from its onClicked handler, I need to find the area from the mouse coordinates. There is a method Context2D.isPointInPath(x,y) which looks promising. It works fine if I have just a single area. Adding a second area returns the same, modified object, and so testing mouse coordinates always reports a mouse click on both areas when clicking the second one, and no hit when clicking on the first one.

Obviously, I'm doing something wrong. This is my ugly proof-of-concept test code, with just two square areas on a square map:

Rectangle { x: 0 y: 0 width: 200 height: 200 border.color: "green" border.width: 1 Canvas { anchors.fill: parent property var p1; property var p2; onPaint: { var ctx = getContext("2d"); ctx.reset(); ctx.lineJoin = "round"; ctx.lineCap = "round"; ctx.strokeStyle = "red"; ctx.fillStyle = "blue"; ctx.lineWidth = 1; p1 = ctx.beginPath(); ctx.moveTo(10,10); ctx.lineTo(90,10); ctx.lineTo(90,90); ctx.lineTo(10,90); ctx.closePath(); ctx.stroke(); ctx.fill() ctx.strokeStyle = "green"; ctx.fillStyle = "yellow"; p2 = ctx.beginPath(); ctx.moveTo(110,110); ctx.lineTo(190,110); ctx.lineTo(190,190); ctx.lineTo(110,190); ctx.closePath(); ctx.stroke(); ctx.fill() } MouseArea { anchors.fill: parent onClicked: function(mouse) { console.log("Mouse at "+mouse.x+", "+mouse.y); if (parent.p1) { console.log("Have p1: "+parent.p1); if (parent.p1.isPointInPath(mouse.x, mouse.y)) + { console.log("Hit the red/blue box"); } } else { console.log("Path p1 not set"); } if (parent.p2) { console.log("Have p2: "+parent.p2); if (parent.p2.isPointInPath(mouse.x, mouse.y)) + { console.log("Hit the yellow/green box"); } } else { console.log("Path p2 not set"); } if (parent.p1 === parent.p2) { console.log("Paths are the same"); // <-- this + happens, but should not } } } } }

Of couse, If I had Qt 5.15 available, I would use Shape and PathSvg, and put MouseAreas into the individual Shapes. But all I have is Qt 5.9.6, Shape was introduced in 5.10.

Any hints?

Alexander

--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Replies are listed 'Best First'.
Re: [OT] QML Canvas Context2D and mouse input
by bliako (Monsignor) on Jan 24, 2023 at 09:51 UTC

    Are you sure beginPath() returns something persistent? For example, if you are using this beginPath(), it returns nothing:

    void QQuickContext2D::beginPath()

    see: https://codebrowser.dev/qt5/qtdeclarative/src/quick/items/context2d/qquickcontext2d.cpp.html#_ZN15QQuickContext2D9beginPathEv

    But you do get some value in p1, p2. This may be the current path, previous paths not persisting.

    If there is a Path constructor then you could create your paths in the Canvas (not via context). Then paint them as you do in onPaint and check inclusion in the MouseArea. But diagonally looking, I did not see any! If none is available, just find+reuse a Shape class with its own PointInShape() method. Then all you have to do in onPaint is to extract the path and supply it to context or supply context to Shape to draw the path.

    bw, bliako

        Looking again at https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-ispointinpath, the example shows a click handler that (partially) draws each clickable object, then invokes isPointInPath() to check if that clickable object contains the mouse coordinates, and finally issues a redraw command, probably to clean up the mess that those checks made.

        That does not look very sane and efficient to me. I'll try that.

        It may be inefficient, but it works.

        Following the example from whatwg.org, I've moved the painting to javascript functions (generated at compile time from the SVG file using Perl and XML::LibXML). This is how it looks in QML:

        import QtQuick 2.0 ... import "mymap.js" as MyMap Rectangle { ... Canvas { id: canvas ... onPaint: { var ctx = getContext("2d"); MyMap.drawBackground(ctx); MyMap.drawAreas(ctx); } MouseArea { anchors.fill: parent onClicked: function(mouse) { var ctx = canvas.getContext("2d"); for (var i = 0; i < MyMap.areaIDs.length; i++) { MyMap.drawArea(ctx, ..., MyMap.areaIDs[i]); if (ctx.isPointInPath(mouse.x, mouse.y)) { console.log("Mouse click at ("+mouse.x+", "+mo +use.y+") hit area "+MyMap.areaIDs[i]); return; } } console.log("Mouse click at ("+mouse.x+", "+mouse.y+") + hit no area"); mouse.accepted = false; } } } }

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11149792]
Approved by marto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (6)
As of 2024-04-25 15:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found