LightmapViewer.qml 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. // Copyright (C) 2025 The Qt Company Ltd.
  2. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
  3. import QtQuick
  4. import QtQuick.Controls
  5. import QtQuick.Layouts
  6. import QtQuick.Window
  7. import QtQuick.Dialogs
  8. import LightmapFile 1.0
  9. ApplicationWindow {
  10. width: 640
  11. height: 480
  12. visible: true
  13. title: qsTr("Lightmap Viewer")
  14. id: window
  15. property string selectedKey: listView.model[0]
  16. property real imageZoom: 1
  17. property real imageCenterX: 0
  18. property real imageCenterY: 0
  19. function clampImagePosition() {
  20. // If the image is smaller than the scroll view, center it
  21. if (image.width <= scrollView.width) {
  22. imageCenterX = 0
  23. } else {
  24. const maxOffsetX = (image.width - scrollView.width) / 2
  25. imageCenterX = Math.max(-maxOffsetX, Math.min(imageCenterX,
  26. maxOffsetX))
  27. }
  28. if (image.height <= scrollView.height) {
  29. imageCenterY = 0
  30. } else {
  31. const maxOffsetY = (image.height - scrollView.height) / 2
  32. imageCenterY = Math.max(-maxOffsetY, Math.min(imageCenterY,
  33. maxOffsetY))
  34. }
  35. }
  36. header: ToolBar {
  37. RowLayout {
  38. Button {
  39. text: qsTr("Open Lightmap")
  40. onClicked: fileDialog.open()
  41. }
  42. Rectangle {
  43. width: 1
  44. color: "darkgray"
  45. Layout.fillHeight: true
  46. Layout.alignment: Qt.AlignVCenter
  47. }
  48. Label {
  49. text: "Zoom: " + window.imageZoom.toFixed(1)
  50. }
  51. Rectangle {
  52. width: 1
  53. color: "darkgray"
  54. Layout.fillHeight: true
  55. Layout.alignment: Qt.AlignVCenter
  56. }
  57. Switch {
  58. id: alphaSwitch
  59. padding: 0
  60. checked: true
  61. text: "Alpha"
  62. }
  63. Rectangle {
  64. width: 1
  65. color: "darkgray"
  66. Layout.fillHeight: true
  67. Layout.alignment: Qt.AlignVCenter
  68. }
  69. Text {
  70. text: "Path: " + LightmapFile.source
  71. }
  72. }
  73. }
  74. FileDialog {
  75. id: fileDialog
  76. onAccepted: {
  77. LightmapFile.source = selectedFile
  78. LightmapFile.loadData()
  79. }
  80. }
  81. Shortcut {
  82. sequences: [StandardKey.Open]
  83. onActivated: {
  84. fileDialog.open()
  85. }
  86. }
  87. SplitView {
  88. anchors.fill: parent
  89. orientation: Qt.Horizontal
  90. focus: true
  91. Keys.onPressed: event => {
  92. if (event.key === Qt.Key_Up) {
  93. listView.currentIndex = Math.max(
  94. 0, listView.currentIndex - 1)
  95. selectedKey = listView.model[listView.currentIndex]
  96. } else if (event.key === Qt.Key_Down) {
  97. listView.currentIndex = Math.min(
  98. listView.model.length - 1,
  99. listView.currentIndex + 1)
  100. selectedKey = listView.model[listView.currentIndex]
  101. }
  102. clampImagePosition()
  103. }
  104. ListView {
  105. id: listView
  106. SplitView.preferredWidth: 100
  107. SplitView.minimumWidth: 50
  108. model: LightmapFile.dataList
  109. delegate: Text {
  110. text: modelData
  111. MouseArea {
  112. anchors.fill: parent
  113. onClicked: {
  114. listView.currentIndex = index
  115. selectedKey = modelData // Select this item
  116. }
  117. }
  118. }
  119. highlight: Rectangle {
  120. color: "lightsteelblue"
  121. radius: 1
  122. }
  123. }
  124. Rectangle {
  125. id: scrollView
  126. clip: true
  127. color: "black"
  128. property real lastMouseX: 0
  129. property real lastMouseY: 0
  130. onWidthChanged: {
  131. clampImagePosition()
  132. }
  133. onHeightChanged: {
  134. clampImagePosition()
  135. }
  136. MouseArea {
  137. id: mouseArea
  138. property bool dragging: false
  139. anchors.fill: parent
  140. onPressed: mouse => {
  141. scrollView.lastMouseX = mouse.x
  142. scrollView.lastMouseY = mouse.y
  143. dragging = true
  144. }
  145. onReleased: mouse => {
  146. dragging = false
  147. }
  148. onPositionChanged: mouse => {
  149. var dx = mouse.x - scrollView.lastMouseX
  150. var dy = mouse.y - scrollView.lastMouseY
  151. scrollView.lastMouseX = mouse.x
  152. scrollView.lastMouseY = mouse.y
  153. imageCenterX += dx
  154. imageCenterY += dy
  155. clampImagePosition()
  156. }
  157. cursorShape: mouseArea.dragging ? Qt.ClosedHandCursor : Qt.ArrowCursor
  158. onWheel: event => {
  159. const oldZoom = imageZoom
  160. const zoomDelta = event.angleDelta.y / 256
  161. const newZoom = Math.max(
  162. 1, Math.min(32, oldZoom + zoomDelta))
  163. if (newZoom === oldZoom)
  164. return
  165. // Adjust center offset so the same point remains at the center
  166. const scaleFactor = newZoom / oldZoom
  167. imageCenterX *= scaleFactor
  168. imageCenterY *= scaleFactor
  169. imageZoom = newZoom
  170. clampImagePosition()
  171. event.accepted = true
  172. }
  173. }
  174. Image {
  175. id: baseGrid
  176. anchors.fill: scrollView
  177. source: "grid.png"
  178. fillMode: Image.Tile
  179. opacity: 0.75
  180. }
  181. Rectangle {
  182. width: image.width + (border.width * 2)
  183. height: image.height + (border.width * 2)
  184. x: image.x - border.width
  185. y: image.y - border.width
  186. color: "white" // This is the border color
  187. border.width: 0
  188. border.color: "white"
  189. opacity: 0.25
  190. }
  191. Image {
  192. id: image
  193. x: Math.round(parent.width / 2 - width / 2) + imageCenterX
  194. y: Math.round(parent.height / 2 - height / 2) + imageCenterY
  195. source: `image://lightmaps/key=${selectedKey}&file=${LightmapFile.source}&alpha=${alphaSwitch.checked}`
  196. onWidthChanged: clampImagePosition()
  197. onHeightChanged: clampImagePosition()
  198. fillMode: Image.PreserveAspectFit
  199. smooth: false
  200. antialiasing: false
  201. // Let the image scale visibly
  202. width: sourceSize.width * imageZoom
  203. height: sourceSize.height * imageZoom
  204. }
  205. }
  206. }
  207. DropArea {
  208. id: dropArea
  209. anchors.fill: parent
  210. onEntered: (drag) => {
  211. drag.accept(Qt.LinkAction)
  212. }
  213. // Just take first url if several
  214. onDropped: (drop) => {
  215. if (drop.hasUrls) {
  216. LightmapFile.source = drop.urls[0]
  217. LightmapFile.loadData()
  218. }
  219. }
  220. }
  221. }