I use Qt/QML Text Item to render a large standalone HTML page with some inner href hash links. and the container height is less than its content height. Everything works fine; I can click the link and see the console log, but I cannot jump the text item's visual position to the href tag position. please help.
The y property of the text item in the container should have been updated when I clicked the href link.
To simplify my question, let me give a minimal reproducible example here.
import QtQuick
Window {
id: root
width: 640
height: 480
visible: true
Rectangle {
id:container
height: root.height*.95
width: root.width*.8
anchors.centerIn: parent
Text {
id: label
width: container.width
textFormat: Text.RichText
wrapMode:Text.WordWrap
onLinkActivated: function(link){
console.log(link)
}
TapHandler {
onTapped:function(eventPoint, button){
ani.running = !ani.running
}
}
SequentialAnimation on y {
id: ani
loops: Animation.Infinite
running: true
PropertyAnimation { to: -label.contentHeight/100;duration: 3000 }
PropertyAnimation { to: 0 ;duration: 3000}
}
}
}
function makeRequest()
{
var doc = new XMLHttpRequest();
label.text = "";
doc.onreadystatechange = function() {
if (doc.readyState === XMLHttpRequest.DONE) {
label.text = doc.responseText;
}
}
doc.open("GET", "https://www.mpfr.org/mpfr-current/mpfr.html");
doc.send();
}
Component.onCompleted: {
// label.text = backEnd.loadFile("C:/ebook/GNU MPFR 4.2.1.htm")
makeRequest()
}
}
and the HTML file can be manually downloaded from the URL link. Now I want to click the blue link, expecting the text item to scroll itself to the exact tag location.
The Text
component lacks the ability to retrieve the coordinates of a specified text position. However, you can use TextEdit
, which includes a positionToRectangle
method that returns a QRect
for the given text position
.
The challenge is that the text position must correspond to plain text, excluding any HTML or XML tags. Therefore, you need to locate the position of the target tag within the formatted text and then map that position to the plain text in the TextEdit
. Since there is no built-in function to accomplish this, I created a helper component to determine the position of the target tag within the plain text.
* Another point I forgot to mention is that the <h1 id="h1">h1</h1>
will be converted to <h1><a name="h1"></a>
after the TextEdit
initialization. Therefore, the search should be for the <a name="${link}"></a>
.
ScrollView {
id: scrollView
contentWidth: width
contentHeight: textedit.implicitHeight
function textgen() {
return `Table of content:<ul id="top"><li><a href="#h1">h1</a></li><li>
<a href="#h2">h2</a></li><li><a href="#h3">h3</a></li><li><a href="#h4">h4</a></li></ul>
<h1 id="h1">h1<sub><a href="#top">top</a></sub></h1>${'sss '.repeat(200)}
<h1 id="h2">h2<sub><a href="#top">top</a></sub></h1>${'sss '.repeat(200)}
<h1 id="h3">h3<sub><a href="#top">top</a></sub></h1>${'sss '.repeat(200)}
<h1 id="h4">h4<sub><a href="#top">top</a></sub></h1>${'sss '.repeat(200)}`;
}
NumberAnimation {
id: animateScroll
target: scrollView.ScrollBar.vertical
property: 'position'
duration: 1000
easing.type: Easing.InOutCubic
}
TextEdit {
id: textedit
property TextEdit helper: TextEdit { textFormat: Text.RichText }
width: root.width
text: scrollView.textgen()
readOnly: true
wrapMode:Text.WordWrap
textFormat: Text.RichText
selectByMouse: false
HoverHandler { cursorShape: textedit.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor }
onLinkActivated: link => {
if(link.startsWith('#')) {
let idx = text.match(`<a name="${link.slice(1)}"></a>`)?.index ?? -1;
if(idx !== -1) {
// Copying the first slice from the beginning to the target index into the helper TextEdit
helper.text = text.slice(0, idx);
// The `helper.length` will represent the target position within the plain text.
const rect = positionToRectangle(helper.length);
animateScroll.to = rect.y/textedit.height;
animateScroll.restart();
}
}
}
}
}