326 lines
10 KiB
QML
326 lines
10 KiB
QML
// 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 QtQuick.Window
|
|
import QtQuick.Dialogs
|
|
import QtQuick3D
|
|
import QtQuick3D.Helpers
|
|
|
|
import QtQuick3D.lightmapviewer
|
|
import LightmapFile 1.0
|
|
|
|
ApplicationWindow {
|
|
width: 1024
|
|
height: 768
|
|
visible: true
|
|
title: qsTr("Lightmap Viewer")
|
|
|
|
id: window
|
|
|
|
property var selectedEntry: listView.model.length ? listView.model[0] : null
|
|
property real imageZoom: 1
|
|
property real imageCenterX: 0
|
|
property real imageCenterY: 0
|
|
|
|
function isImage(entry) {
|
|
return entry && entry.kind === "image"
|
|
}
|
|
function isMesh(entry) {
|
|
return entry && entry.kind === "mesh"
|
|
}
|
|
|
|
Dialog {
|
|
id: sceneMetadataDialog
|
|
modal: true
|
|
standardButtons: Dialog.NoButton
|
|
x: Math.round((window.width - width) / 2)
|
|
y: Math.round((window.height - height) / 2)
|
|
visible: false
|
|
width: 220
|
|
height: 360
|
|
|
|
contentItem: SceneMetadataView {}
|
|
}
|
|
|
|
header: ToolBar {
|
|
RowLayout {
|
|
Button {
|
|
text: qsTr("Open Lightmap...")
|
|
onClicked: fileDialog.open()
|
|
}
|
|
|
|
Rectangle {
|
|
width: 1
|
|
color: "darkgray"
|
|
Layout.fillHeight: true
|
|
Layout.alignment: Qt.AlignVCenter
|
|
}
|
|
|
|
Button {
|
|
text: qsTr("Scene Metadata...")
|
|
onClicked: sceneMetadataDialog.open()
|
|
}
|
|
|
|
Label {
|
|
text: "Zoom: " + window.imageZoom.toFixed(1)
|
|
}
|
|
|
|
Rectangle {
|
|
width: 1
|
|
color: "darkgray"
|
|
Layout.fillHeight: true
|
|
Layout.alignment: Qt.AlignVCenter
|
|
}
|
|
|
|
Switch {
|
|
id: alphaSwitch
|
|
padding: 0
|
|
checked: true
|
|
text: "Alpha"
|
|
}
|
|
|
|
Rectangle {
|
|
width: 1
|
|
color: "darkgray"
|
|
Layout.fillHeight: true
|
|
Layout.alignment: Qt.AlignVCenter
|
|
}
|
|
|
|
Text {
|
|
text: "Path: " + LightmapFile.source
|
|
}
|
|
}
|
|
}
|
|
|
|
FileDialog {
|
|
id: fileDialog
|
|
onAccepted: {
|
|
LightmapFile.source = selectedFile
|
|
LightmapFile.loadData()
|
|
}
|
|
}
|
|
|
|
Shortcut {
|
|
sequences: [StandardKey.Open]
|
|
onActivated: {
|
|
fileDialog.open()
|
|
}
|
|
}
|
|
|
|
SplitView {
|
|
anchors.fill: parent
|
|
orientation: Qt.Horizontal
|
|
|
|
focus: true
|
|
Keys.onPressed: event => {
|
|
if (event.key === Qt.Key_Up) {
|
|
listView.currentIndex = Math.max(
|
|
0, listView.currentIndex - 1)
|
|
selectedEntry = listView.model[listView.currentIndex]
|
|
} else if (event.key === Qt.Key_Down) {
|
|
listView.currentIndex = Math.min(
|
|
listView.model.length - 1,
|
|
listView.currentIndex + 1)
|
|
selectedEntry = listView.model[listView.currentIndex]
|
|
}
|
|
}
|
|
|
|
SplitView {
|
|
id: leftSplit
|
|
SplitView.preferredWidth: 220
|
|
SplitView.minimumWidth: 120
|
|
orientation: Qt.Vertical
|
|
|
|
Item {
|
|
id: metaArea
|
|
SplitView.preferredHeight: 120
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
spacing: 4
|
|
|
|
Pane {
|
|
id: metaPane
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
clip: true
|
|
|
|
ScrollView {
|
|
anchors.fill: parent
|
|
|
|
ColumnLayout {
|
|
id: metadataColumn
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
|
|
Repeater {
|
|
model: LightmapFile.metadataFor(selectedEntry)
|
|
delegate: RowLayout {
|
|
width: metadataColumn.width
|
|
spacing: 8
|
|
|
|
Label {
|
|
text: (modelData.key ?? "—") + ":"
|
|
font.bold: true
|
|
}
|
|
Label {
|
|
text: modelData.value
|
|
!== undefined ? String(
|
|
modelData.value) : "—"
|
|
Layout.fillWidth: true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ListView {
|
|
id: listView
|
|
clip: true
|
|
spacing: 2
|
|
highlightMoveVelocity: -1
|
|
highlightMoveDuration: 1
|
|
model: LightmapFile.dataList
|
|
property var sectionExpanded: ({})
|
|
|
|
section.property: "owner"
|
|
section.criteria: ViewSection.FullString
|
|
section.delegate: Rectangle {
|
|
width: listView.width
|
|
height: 26
|
|
color: Qt.rgba(0, 0, 0, 0.05)
|
|
radius: 4
|
|
|
|
Row {
|
|
anchors.fill: parent
|
|
anchors.leftMargin: 8
|
|
anchors.rightMargin: 8
|
|
spacing: 6
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
Text {
|
|
text: (listView.sectionExpanded[section] === false) ? "▸" : "▾"
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
Text {
|
|
text: section
|
|
font.bold: true
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onClicked: {
|
|
listView.sectionExpanded[section]
|
|
= !(listView.sectionExpanded[section] !== false)
|
|
listView.sectionExpanded = Object.assign(
|
|
{}, listView.sectionExpanded)
|
|
}
|
|
}
|
|
}
|
|
|
|
delegate: Item {
|
|
width: listView.width
|
|
|
|
property bool isExpanded: listView.sectionExpanded[modelData.owner] !== false
|
|
|
|
height: isExpanded ? Math.max(
|
|
24, rowText.implicitHeight + 6) : 0
|
|
opacity: isExpanded ? 1 : 0
|
|
|
|
Behavior on height {
|
|
NumberAnimation {
|
|
duration: 120
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
Behavior on opacity {
|
|
NumberAnimation {
|
|
duration: 120
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
|
|
Row {
|
|
anchors.fill: parent
|
|
anchors.leftMargin: 16
|
|
anchors.rightMargin: 8
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
Text {
|
|
id: rowText
|
|
text: modelData.display
|
|
elide: Text.ElideRight
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
enabled: isExpanded
|
|
onClicked: {
|
|
listView.currentIndex = index
|
|
selectedEntry = modelData
|
|
}
|
|
}
|
|
}
|
|
|
|
highlight: Rectangle {
|
|
color: Qt.rgba(76 / 255, 134 / 255, 191 / 255, 0.25)
|
|
radius: 6
|
|
anchors.margins: 2
|
|
}
|
|
|
|
ScrollBar.vertical: ScrollBar {}
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: rightSplit
|
|
SplitView.fillWidth: true
|
|
SplitView.fillHeight: true
|
|
|
|
// These are toggled based on what is currently selected
|
|
Loader {
|
|
id: imageLoader
|
|
anchors.fill: parent
|
|
sourceComponent: ImageViewer {}
|
|
active: true
|
|
visible: isImage(selectedEntry)
|
|
enabled: visible
|
|
}
|
|
|
|
Loader {
|
|
id: meshLoader
|
|
anchors.fill: parent
|
|
sourceComponent: MeshViewer {}
|
|
active: true
|
|
visible: isMesh(selectedEntry)
|
|
enabled: visible
|
|
}
|
|
}
|
|
}
|
|
|
|
DropArea {
|
|
id: dropArea
|
|
anchors.fill: parent
|
|
onEntered: drag => {
|
|
drag.accept(Qt.LinkAction)
|
|
}
|
|
// Just take first url if several
|
|
onDropped: drop => {
|
|
if (drop.hasUrls) {
|
|
LightmapFile.source = drop.urls[0]
|
|
LightmapFile.loadData()
|
|
}
|
|
}
|
|
}
|
|
}
|