300 lines
10 KiB
QML
300 lines
10 KiB
QML
|
|
// ==========================================================
|
|||
|
|
// =============== 可显示OCR文本的增强Image组件 ===============
|
|||
|
|
// ==========================================================
|
|||
|
|
|
|||
|
|
import QtQuick 2.15
|
|||
|
|
import QtQuick.Controls 2.15
|
|||
|
|
import ".."
|
|||
|
|
|
|||
|
|
ImageScale {
|
|||
|
|
id: iRoot
|
|||
|
|
property bool showOverlay: true // 显示叠加层
|
|||
|
|
|
|||
|
|
Component.onCompleted: {
|
|||
|
|
// 默认显示/关闭叠加层
|
|||
|
|
showOverlay = qmlapp.globalConfigs.getValue("ui.imgShowOverlay")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
beforeShow: () => {
|
|||
|
|
mouseArea.initIndex() // 清空选字参数
|
|||
|
|
textBoxes = [] // 清空旧文本块
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 展示文本块
|
|||
|
|
function showTextBoxes(res) {
|
|||
|
|
beforeShow()
|
|||
|
|
// 提取文本框
|
|||
|
|
if(res.code == 100 && res.data.length > 0) {
|
|||
|
|
let tbs = []
|
|||
|
|
for(let i in res.data) {
|
|||
|
|
const d = res.data[i]
|
|||
|
|
const info = {
|
|||
|
|
x: d.box[0][0],
|
|||
|
|
y: d.box[0][1],
|
|||
|
|
width: d.box[2][0] - d.box[0][0],
|
|||
|
|
height: d.box[2][1] - d.box[0][1],
|
|||
|
|
text: d.text,
|
|||
|
|
end: d.end || "", // 行尾间隔符
|
|||
|
|
}
|
|||
|
|
tbs.push(info)
|
|||
|
|
}
|
|||
|
|
textBoxes = tbs
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 弹出菜单
|
|||
|
|
function popupMenu() {
|
|||
|
|
selectMenu.popup()
|
|||
|
|
}
|
|||
|
|
// 显示/隐藏叠加层
|
|||
|
|
function switchOverlay() {
|
|||
|
|
showOverlay = !showOverlay
|
|||
|
|
if(!showOverlay)
|
|||
|
|
mouseArea.cursorShape = Qt.OpenHandCursor
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
property var textBoxes: [] // 文本块列表
|
|||
|
|
|
|||
|
|
// 文本块叠加层
|
|||
|
|
overlayLayer: Item {
|
|||
|
|
id: oRoot
|
|||
|
|
anchors.fill: parent
|
|||
|
|
visible: showOverlay
|
|||
|
|
|
|||
|
|
Repeater {
|
|||
|
|
id: textBoxRepeater
|
|||
|
|
model: textBoxes
|
|||
|
|
TextBox {
|
|||
|
|
text: modelData.text
|
|||
|
|
x: modelData.x
|
|||
|
|
y: modelData.y
|
|||
|
|
end: modelData.end // 结尾间隔符
|
|||
|
|
|
|||
|
|
Component.onCompleted: {
|
|||
|
|
width = modelData.width
|
|||
|
|
height = modelData.height
|
|||
|
|
resetSize() // 自适应字体和组件大小
|
|||
|
|
textBoxes[index].width = width // 记录修改后的组件大小
|
|||
|
|
textBoxes[index].height = height
|
|||
|
|
textBoxes[index].obj = this
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
MouseArea {
|
|||
|
|
id: mouseArea
|
|||
|
|
anchors.fill: parent
|
|||
|
|
hoverEnabled: true
|
|||
|
|
property int startIndex: -1 // 拖拽开始时,文本框序号
|
|||
|
|
property int startTextIndex: -1 // 拖拽开始时,字符序号
|
|||
|
|
property int endIndex: -1 // 拖拽结束时,文本框序号
|
|||
|
|
property int endTextIndex: -1 // 拖拽结束时,字符序号
|
|||
|
|
property int selectUpdate: 0
|
|||
|
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|||
|
|
|
|||
|
|
// 清除index
|
|||
|
|
function initIndex() {
|
|||
|
|
startIndex = startTextIndex = endIndex = endTextIndex = -1
|
|||
|
|
}
|
|||
|
|
// 检测当前鼠标点,位于哪一个tb内
|
|||
|
|
function mouseInTextBox() {
|
|||
|
|
const localPoint = oRoot.mapFromItem(mouseArea, mouseX, mouseY)
|
|||
|
|
const x = localPoint.x, y = localPoint.y
|
|||
|
|
for (let i=0,l=textBoxes.length; i<l; i++) {
|
|||
|
|
const rect = textBoxes[i]
|
|||
|
|
if (x >= rect.x && x <= rect.x + rect.width &&
|
|||
|
|
y >= rect.y && y <= rect.y + rect.height) {
|
|||
|
|
return i
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return -1
|
|||
|
|
}
|
|||
|
|
// 检测当前鼠标点,在index tb内的哪一个字符处
|
|||
|
|
function mouseInTextIndex(index) {
|
|||
|
|
return textBoxes[index].obj.where(mouseArea, mouseX, mouseY)
|
|||
|
|
}
|
|||
|
|
// 获取Index正确顺序。返回: [li 起始块, lt 起始块选区左侧, ri 结束块, rt 结束块选区右侧]
|
|||
|
|
function getIndexes() {
|
|||
|
|
let li, lt, ri, rt
|
|||
|
|
if(startIndex < endIndex) {
|
|||
|
|
li=startIndex; lt=startTextIndex; ri=endIndex; rt=endTextIndex;
|
|||
|
|
}
|
|||
|
|
else if(startIndex > endIndex) {
|
|||
|
|
li=endIndex; lt=endTextIndex; ri=startIndex; rt=startTextIndex;
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
li = ri = startIndex
|
|||
|
|
if(startTextIndex < endTextIndex) {
|
|||
|
|
lt=startTextIndex; rt=endTextIndex;
|
|||
|
|
}
|
|||
|
|
else if(startTextIndex > endTextIndex) {
|
|||
|
|
lt=endTextIndex; rt=startTextIndex;
|
|||
|
|
}
|
|||
|
|
else { // 单击,未选中
|
|||
|
|
lt = rt = -1
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return [li, lt, ri, rt]
|
|||
|
|
}
|
|||
|
|
// 根据 Index 的参数,选择对应文本。
|
|||
|
|
function selectIndex() {
|
|||
|
|
const [li, lt, ri, rt] = getIndexes()
|
|||
|
|
// 遍历每个文本框数据
|
|||
|
|
for (let i = 0, l=textBoxes.length; i < l; i++) {
|
|||
|
|
const tEdit = textBoxes[i].obj.textEdit
|
|||
|
|
if( li<0 || ri<0 || i<li || i>ri ) { // 未被选中
|
|||
|
|
tEdit.deselect()
|
|||
|
|
}
|
|||
|
|
else if(i === li && i === ri) { // 单个块
|
|||
|
|
if(lt === rt) // 无有效选中
|
|||
|
|
tEdit.deselect()
|
|||
|
|
else
|
|||
|
|
tEdit.select(lt, rt)
|
|||
|
|
}
|
|||
|
|
else if(i === li) { // 多个块的起始
|
|||
|
|
const len = textBoxes[i].text.length
|
|||
|
|
tEdit.select(lt, len)
|
|||
|
|
}
|
|||
|
|
else if(i === ri) { // 多个块的结束
|
|||
|
|
tEdit.select(0, rt)
|
|||
|
|
}
|
|||
|
|
else { // 多个块的中间
|
|||
|
|
tEdit.selectAll(0, rt)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 全选
|
|||
|
|
function selectAll() {
|
|||
|
|
const l = textBoxes.length
|
|||
|
|
if(l === 0) return
|
|||
|
|
startIndex = startTextIndex = 0
|
|||
|
|
endIndex = l-1
|
|||
|
|
endTextIndex = textBoxes[endIndex].text.length
|
|||
|
|
selectIndex()
|
|||
|
|
}
|
|||
|
|
// 复制已选中的内容
|
|||
|
|
function selectCopy() {
|
|||
|
|
let [li, lt, ri, rt] = getIndexes()
|
|||
|
|
// 没有有效选中,则复制全部
|
|||
|
|
if(li<0 || ri<0 || (li===ri && lt===rt)) {
|
|||
|
|
selectAllCopy()
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
let copyText = ""
|
|||
|
|
// 选中单个文本块
|
|||
|
|
if(li === ri) {
|
|||
|
|
copyText = textBoxes[li].text.substring(lt, rt)
|
|||
|
|
}
|
|||
|
|
// 选中多个块,则遍历多个块,提取各自的文本
|
|||
|
|
else {
|
|||
|
|
for(let i = li; i <= ri; i++) {
|
|||
|
|
const text = textBoxes[i].text
|
|||
|
|
const end = textBoxes[i].end
|
|||
|
|
if(i === li) // 多个块的起始
|
|||
|
|
copyText = text.substring(lt) + end
|
|||
|
|
else if(i === ri) // 多个块的结束
|
|||
|
|
copyText += text.substring(0, rt)
|
|||
|
|
else // 多个块的中间
|
|||
|
|
copyText += text + end
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if(copyText && copyText.length > 0) {
|
|||
|
|
qmlapp.utilsConnector.copyText(copyText)
|
|||
|
|
qmlapp.popup.simple(qsTr("图片:复制%1字").arg(copyText.length), "")
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
qmlapp.popup.simple(qsTr("图片:无选中文字"), "")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 复制所有
|
|||
|
|
function selectAllCopy() {
|
|||
|
|
let copyText = ""
|
|||
|
|
for (let i = 0, l=textBoxes.length; i < l; i++) {
|
|||
|
|
copyText += textBoxes[i].text
|
|||
|
|
if(i < l-1) copyText += textBoxes[i].end
|
|||
|
|
}
|
|||
|
|
qmlapp.utilsConnector.copyText(copyText)
|
|||
|
|
qmlapp.popup.simple(qsTr("图片:复制全部%1字").arg(copyText.length), "")
|
|||
|
|
selectAll()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按下
|
|||
|
|
onPressed: {
|
|||
|
|
mouseArea.forceActiveFocus()
|
|||
|
|
if (mouse.button === Qt.RightButton) {
|
|||
|
|
selectMenu.popup()
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
if(!showOverlay) {
|
|||
|
|
mouse.accepted = false
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
initIndex()
|
|||
|
|
const tbi = mouseInTextBox()
|
|||
|
|
cursorShape = tbi < 0 ? Qt.ClosedHandCursor : Qt.IBeamCursor
|
|||
|
|
if(tbi >= 0) { // 选择文本
|
|||
|
|
startIndex = tbi
|
|||
|
|
startTextIndex = mouseInTextIndex(tbi)
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
mouse.accepted = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 移动
|
|||
|
|
onPositionChanged: {
|
|||
|
|
if(!showOverlay) return
|
|||
|
|
const tbi = mouseInTextBox()
|
|||
|
|
if(pressed) { // 拖拽中
|
|||
|
|
if(tbi >= 0) { // 选择文本
|
|||
|
|
endIndex = tbi
|
|||
|
|
endTextIndex = mouseInTextIndex(tbi)
|
|||
|
|
selectIndex()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else { // 悬停中
|
|||
|
|
cursorShape = tbi < 0 ? Qt.OpenHandCursor : Qt.IBeamCursor
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 抬起
|
|||
|
|
onReleased: {
|
|||
|
|
if(!showOverlay) return
|
|||
|
|
if (mouse.button === Qt.RightButton) return
|
|||
|
|
const tbi = mouseInTextBox()
|
|||
|
|
cursorShape = tbi < 0 ? Qt.OpenHandCursor : Qt.IBeamCursor
|
|||
|
|
if(startIndex === -1) return
|
|||
|
|
if(tbi >= 0) {
|
|||
|
|
endIndex = tbi
|
|||
|
|
endTextIndex = mouseInTextIndex(tbi)
|
|||
|
|
}
|
|||
|
|
selectIndex()
|
|||
|
|
}
|
|||
|
|
// 菜单
|
|||
|
|
Menu_ {
|
|||
|
|
id: selectMenu
|
|||
|
|
menuList: [
|
|||
|
|
[mouseArea.selectCopy, qsTr("复制 (Ctrl+C)")],
|
|||
|
|
[mouseArea.selectAll, qsTr("全选 (Ctrl+A)")],
|
|||
|
|
[iRoot.copyImage, qsTr("复制图片(Ctrl+X)")],
|
|||
|
|
[iRoot.saveImage, qsTr("保存图片(Ctrl+S)")],
|
|||
|
|
[iRoot.switchOverlay, qsTr("显示/隐藏文字(Tab)")],
|
|||
|
|
[iRoot.openImage, qsTr("用默认应用打开图片")],
|
|||
|
|
[iRoot.clear, qsTr("删除图片(Ctrl+D)"), "noColor"],
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
// 按键事件
|
|||
|
|
Keys.onPressed: {
|
|||
|
|
if (event.modifiers & Qt.ControlModifier) {
|
|||
|
|
event.key===Qt.Key_A && selectAll()
|
|||
|
|
event.key===Qt.Key_C && selectCopy()
|
|||
|
|
event.key===Qt.Key_X && iRoot.copyImage()
|
|||
|
|
event.key===Qt.Key_S && iRoot.saveImage()
|
|||
|
|
event.key===Qt.Key_D && iRoot.clear()
|
|||
|
|
}
|
|||
|
|
if (event.key === Qt.Key_Tab) {
|
|||
|
|
iRoot.switchOverlay()
|
|||
|
|
Qt.callLater(mouseArea.forceActiveFocus)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|