Revision control

Copy as Markdown

// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import LightmapFile 1.0
ColumnLayout {
id: root
anchors.fill: parent
property real imageZoom: 1
property real imageCenterX: 0
property real imageCenterY: 0
Pane {
Layout.alignment: Qt.AlignLeft
implicitHeight: 30
padding: 8
RowLayout {
spacing: 20
Label {
text: "Zoom: " + root.imageZoom.toFixed(1)
}
Switch {
id: alphaSwitch
padding: 0
checked: true
text: "Alpha"
}
}
}
Rectangle {
id: scrollView
clip: true
color: "black"
Layout.fillWidth: true
Layout.fillHeight: true
property real lastMouseX: 0
property real lastMouseY: 0
function clamp() {
// If the image is smaller than the scroll view, center it
if (image.width <= scrollView.width) {
root.imageCenterX = 0
} else {
const maxOffsetX = (image.width - scrollView.width) / 2
root.imageCenterX = Math.max(-maxOffsetX,
Math.min(root.imageCenterX,
maxOffsetX))
}
if (image.height <= scrollView.height) {
root.imageCenterY = 0
} else {
const maxOffsetY = (image.height - scrollView.height) / 2
root.imageCenterY = Math.max(-maxOffsetY,
Math.min(root.imageCenterY,
maxOffsetY))
}
}
onWidthChanged: scrollView.clamp()
onHeightChanged: scrollView.clamp()
Connections {
target: window
function onSelectedKeyChanged() {
if (imageLoader.item === scrollView) {
root.imageZoom = 1
root.imageCenterX = 0
root.imageCenterY = 0
}
}
}
MouseArea {
id: mouseArea
property bool dragging: false
anchors.fill: parent
onPressed: mouse => {
scrollView.lastMouseX = mouse.x
scrollView.lastMouseY = mouse.y
dragging = true
}
onReleased: mouse => {
dragging = false
}
onPositionChanged: mouse => {
var dx = mouse.x - scrollView.lastMouseX
var dy = mouse.y - scrollView.lastMouseY
scrollView.lastMouseX = mouse.x
scrollView.lastMouseY = mouse.y
root.imageCenterX += dx
root.imageCenterY += dy
scrollView.clamp()
}
cursorShape: mouseArea.dragging ? Qt.ClosedHandCursor : Qt.ArrowCursor
onWheel: event => {
const oldZoom = root.imageZoom
const zoomDelta = event.angleDelta.y / 256
const newZoom = Math.max(1,
Math.min(32,
oldZoom + zoomDelta))
if (newZoom === oldZoom)
return
// Adjust center offset so the same point remains at the center
const scaleFactor = newZoom / oldZoom
root.imageCenterX *= scaleFactor
root.imageCenterY *= scaleFactor
root.imageZoom = newZoom
scrollView.clamp()
event.accepted = true
}
}
Image {
id: baseGrid
anchors.fill: scrollView
source: "grid.png"
fillMode: Image.Tile
opacity: 0.75
}
Rectangle {
width: image.width + (border.width * 2)
height: image.height + (border.width * 2)
x: image.x - border.width
y: image.y - border.width
color: "white"
border.width: 0
border.color: "white"
opacity: 0.25
}
Image {
id: image
x: Math.round(parent.width / 2 - width / 2) + root.imageCenterX
y: Math.round(parent.height / 2 - height / 2) + root.imageCenterY
source: LightmapFile.imageUrlFor(selectedKey, selectedTextureTag,
alphaSwitch.checked)
onWidthChanged: scrollView.clamp()
onHeightChanged: scrollView.clamp()
fillMode: Image.PreserveAspectFit
smooth: false
antialiasing: false
// Let the image scale visibly
width: sourceSize.width * root.imageZoom
height: sourceSize.height * root.imageZoom
}
}
}