HUDTop.qml 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. import QtQuick 2.15
  2. import QtQuick.Controls 2.15
  3. import QtQuick.Layouts 2.15
  4. Item {
  5. id: topRoot
  6. property bool gameIsPaused: false
  7. property real currentSpeed: 1
  8. readonly property int barmin_height: 72
  9. readonly property bool compact: width < 800
  10. readonly property bool ultraCompact: width < 560
  11. signal pauseToggled()
  12. signal speedChanged(real speed)
  13. Rectangle {
  14. id: topPanel
  15. anchors.left: parent.left
  16. anchors.right: parent.right
  17. anchors.top: parent.top
  18. height: barmin_height
  19. color: "#1a1a1a"
  20. opacity: 0.98
  21. clip: true
  22. Rectangle {
  23. anchors.fill: parent
  24. opacity: 0.9
  25. gradient: Gradient {
  26. GradientStop {
  27. position: 0
  28. color: "#22303a"
  29. }
  30. GradientStop {
  31. position: 1
  32. color: "#0f1a22"
  33. }
  34. }
  35. }
  36. Rectangle {
  37. anchors.left: parent.left
  38. anchors.right: parent.right
  39. anchors.bottom: parent.bottom
  40. height: 2
  41. gradient: Gradient {
  42. GradientStop {
  43. position: 0
  44. color: "transparent"
  45. }
  46. GradientStop {
  47. position: 0.5
  48. color: "#3498db"
  49. }
  50. GradientStop {
  51. position: 1
  52. color: "transparent"
  53. }
  54. }
  55. }
  56. RowLayout {
  57. id: barRow
  58. anchors.fill: parent
  59. anchors.margins: 8
  60. spacing: 12
  61. RowLayout {
  62. id: leftGroup
  63. spacing: 10
  64. Layout.alignment: Qt.AlignVCenter
  65. Button {
  66. id: pauseBtn
  67. Layout.preferredWidth: topRoot.compact ? 48 : 56
  68. Layout.preferredHeight: Math.min(40, topPanel.height - 12)
  69. text: topRoot.gameIsPaused ? "\u25B6" : "\u23F8"
  70. font.pixelSize: 26
  71. font.bold: true
  72. focusPolicy: Qt.NoFocus
  73. onClicked: topRoot.pauseToggled()
  74. background: Rectangle {
  75. color: parent.pressed ? "#e74c3c" : parent.hovered ? "#c0392b" : "#34495e"
  76. radius: 6
  77. border.color: "#2c3e50"
  78. border.width: 1
  79. }
  80. contentItem: Text {
  81. text: parent.text
  82. font: parent.font
  83. color: "#ecf0f1"
  84. horizontalAlignment: Text.AlignHCenter
  85. verticalAlignment: Text.AlignVCenter
  86. }
  87. }
  88. Rectangle {
  89. width: 2
  90. Layout.fillHeight: true
  91. radius: 1
  92. visible: !topRoot.compact
  93. gradient: Gradient {
  94. GradientStop {
  95. position: 0
  96. color: "transparent"
  97. }
  98. GradientStop {
  99. position: 0.5
  100. color: "#34495e"
  101. }
  102. GradientStop {
  103. position: 1
  104. color: "transparent"
  105. }
  106. }
  107. }
  108. RowLayout {
  109. spacing: 8
  110. Layout.alignment: Qt.AlignVCenter
  111. Label {
  112. text: qsTr("Speed:")
  113. visible: !topRoot.compact
  114. color: "#ecf0f1"
  115. font.pixelSize: 14
  116. font.bold: true
  117. verticalAlignment: Text.AlignVCenter
  118. }
  119. Row {
  120. id: speedRow
  121. property var options: [0.5, 1, 2]
  122. spacing: 8
  123. visible: !topRoot.compact
  124. ButtonGroup {
  125. id: speedGroup
  126. }
  127. Repeater {
  128. model: speedRow.options
  129. delegate: Button {
  130. Layout.minimumWidth: 48
  131. width: 56
  132. height: Math.min(34, topPanel.height - 16)
  133. checkable: true
  134. enabled: !topRoot.gameIsPaused
  135. checked: (topRoot.currentSpeed === modelData) && !topRoot.gameIsPaused
  136. focusPolicy: Qt.NoFocus
  137. text: modelData + "x"
  138. ButtonGroup.group: speedGroup
  139. onClicked: topRoot.speedChanged(modelData)
  140. background: Rectangle {
  141. color: parent.checked ? "#27ae60" : parent.hovered ? "#34495e" : "#2c3e50"
  142. radius: 6
  143. border.color: parent.checked ? "#229954" : "#1a252f"
  144. border.width: 1
  145. }
  146. contentItem: Text {
  147. text: parent.text
  148. font.pixelSize: 13
  149. font.bold: true
  150. color: parent.enabled ? "#ecf0f1" : "#7f8c8d"
  151. horizontalAlignment: Text.AlignHCenter
  152. verticalAlignment: Text.AlignVCenter
  153. }
  154. }
  155. }
  156. }
  157. ComboBox {
  158. id: speedCombo
  159. visible: topRoot.compact
  160. Layout.preferredWidth: 120
  161. model: ["0.5x", "1x", "2x"]
  162. currentIndex: topRoot.currentSpeed === 0.5 ? 0 : topRoot.currentSpeed === 1 ? 1 : 2
  163. enabled: !topRoot.gameIsPaused
  164. font.pixelSize: 13
  165. onActivated: function(i) {
  166. var v = i === 0 ? 0.5 : (i === 1 ? 1 : 2);
  167. topRoot.speedChanged(v);
  168. }
  169. }
  170. }
  171. Rectangle {
  172. width: 2
  173. Layout.fillHeight: true
  174. radius: 1
  175. visible: !topRoot.compact
  176. gradient: Gradient {
  177. GradientStop {
  178. position: 0
  179. color: "transparent"
  180. }
  181. GradientStop {
  182. position: 0.5
  183. color: "#34495e"
  184. }
  185. GradientStop {
  186. position: 1
  187. color: "transparent"
  188. }
  189. }
  190. }
  191. RowLayout {
  192. spacing: 8
  193. Layout.alignment: Qt.AlignVCenter
  194. Label {
  195. text: qsTr("Camera:")
  196. visible: !topRoot.compact
  197. color: "#ecf0f1"
  198. font.pixelSize: 14
  199. font.bold: true
  200. verticalAlignment: Text.AlignVCenter
  201. }
  202. Button {
  203. id: followBtn
  204. Layout.preferredWidth: topRoot.compact ? 44 : 80
  205. Layout.preferredHeight: Math.min(34, topPanel.height - 16)
  206. checkable: true
  207. text: topRoot.compact ? "\u2609" : qsTr("Follow")
  208. font.pixelSize: 13
  209. focusPolicy: Qt.NoFocus
  210. onToggled: {
  211. if (typeof game !== 'undefined' && game.camera_follow_selection)
  212. game.camera_follow_selection(checked);
  213. }
  214. background: Rectangle {
  215. color: parent.checked ? "#3498db" : parent.hovered ? "#34495e" : "#2c3e50"
  216. radius: 6
  217. border.color: parent.checked ? "#2980b9" : "#1a252f"
  218. border.width: 1
  219. }
  220. contentItem: Text {
  221. text: parent.text
  222. font: parent.font
  223. color: "#ecf0f1"
  224. horizontalAlignment: Text.AlignHCenter
  225. verticalAlignment: Text.AlignVCenter
  226. }
  227. }
  228. Button {
  229. id: resetBtn
  230. Layout.preferredWidth: topRoot.compact ? 44 : 80
  231. Layout.preferredHeight: Math.min(34, topPanel.height - 16)
  232. text: topRoot.compact ? "\u21BA" : qsTr("Reset")
  233. font.pixelSize: 13
  234. focusPolicy: Qt.NoFocus
  235. onClicked: {
  236. if (typeof game !== 'undefined' && game.reset_camera)
  237. game.reset_camera();
  238. }
  239. background: Rectangle {
  240. color: parent.hovered ? "#34495e" : "#2c3e50"
  241. radius: 6
  242. border.color: "#1a252f"
  243. border.width: 1
  244. }
  245. contentItem: Text {
  246. text: parent.text
  247. font: parent.font
  248. color: "#ecf0f1"
  249. horizontalAlignment: Text.AlignHCenter
  250. verticalAlignment: Text.AlignVCenter
  251. }
  252. }
  253. }
  254. }
  255. Item {
  256. Layout.fillWidth: true
  257. }
  258. RowLayout {
  259. id: rightGroup
  260. spacing: 12
  261. Layout.alignment: Qt.AlignVCenter
  262. Layout.rightMargin: 260
  263. Row {
  264. id: statsRow
  265. spacing: 10
  266. Layout.alignment: Qt.AlignVCenter
  267. Label {
  268. id: playerLbl
  269. text: "🗡️ " + (typeof game !== 'undefined' ? game.player_troop_count : 0) + " / " + (typeof game !== 'undefined' ? game.max_troops_per_player : 0)
  270. color: {
  271. if (typeof game === 'undefined')
  272. return "#95a5a6";
  273. var count = game.player_troop_count;
  274. var max = game.max_troops_per_player;
  275. if (count >= max)
  276. return "#e74c3c";
  277. if (count >= max * 0.8)
  278. return "#f39c12";
  279. return "#2ecc71";
  280. }
  281. font.pixelSize: 14
  282. font.bold: true
  283. elide: Text.ElideRight
  284. verticalAlignment: Text.AlignVCenter
  285. }
  286. Rectangle {
  287. width: 2
  288. height: 24
  289. color: "#34495e"
  290. opacity: 0.5
  291. visible: !topRoot.compact
  292. }
  293. Label {
  294. id: ownersLbl
  295. text: {
  296. if (typeof game === 'undefined')
  297. return "Players: 0";
  298. var owners = game.owner_info;
  299. var playerCount = 0;
  300. var aiCount = 0;
  301. for (var i = 0; i < owners.length; i++) {
  302. if (owners[i].type === "Player")
  303. playerCount++;
  304. else if (owners[i].type === "AI")
  305. aiCount++;
  306. }
  307. return "👥 " + playerCount + " | 🤖 " + aiCount;
  308. }
  309. color: "#ecf0f1"
  310. font.pixelSize: 13
  311. font.bold: false
  312. visible: !topRoot.compact
  313. verticalAlignment: Text.AlignVCenter
  314. ToolTip.visible: ma.containsMouse
  315. ToolTip.delay: 500
  316. ToolTip.text: {
  317. if (typeof game === 'undefined')
  318. return "";
  319. var owners = game.owner_info;
  320. var tip = "Owner IDs:\n";
  321. for (var i = 0; i < owners.length; i++) {
  322. tip += owners[i].id + ": " + owners[i].name + " (" + owners[i].type + ")";
  323. if (owners[i].isLocal)
  324. tip += " [You]";
  325. tip += "\n";
  326. }
  327. return tip;
  328. }
  329. MouseArea {
  330. id: ma
  331. anchors.fill: parent
  332. hoverEnabled: true
  333. }
  334. }
  335. Label {
  336. id: enemyLbl
  337. text: "💀 " + (typeof game !== 'undefined' ? game.enemy_troops_defeated : 0)
  338. color: "#ecf0f1"
  339. font.pixelSize: 14
  340. elide: Text.ElideRight
  341. verticalAlignment: Text.AlignVCenter
  342. }
  343. }
  344. Item {
  345. id: miniWrap
  346. visible: !topRoot.ultraCompact
  347. Layout.preferredWidth: Math.round(topPanel.height * 2.2)
  348. Layout.minimumWidth: Math.round(topPanel.height * 1.6)
  349. Layout.preferredHeight: topPanel.height - 8
  350. }
  351. }
  352. }
  353. }
  354. Rectangle {
  355. id: minimapContainer
  356. visible: !topRoot.ultraCompact
  357. width: 240
  358. height: 240
  359. anchors.right: parent.right
  360. anchors.top: parent.top
  361. anchors.rightMargin: 8
  362. anchors.topMargin: 8
  363. z: 100
  364. color: "#0f1a22"
  365. radius: 8
  366. border.width: 2
  367. border.color: "#3498db"
  368. Rectangle {
  369. anchors.fill: parent
  370. anchors.margins: 3
  371. radius: 6
  372. color: "#0a0f14"
  373. Image {
  374. id: minimapImage
  375. property int imageVersion: 0
  376. anchors.fill: parent
  377. anchors.margins: 2
  378. source: imageVersion > 0 ? "image://minimap/v" + imageVersion : ""
  379. fillMode: Image.PreserveAspectFit
  380. smooth: true
  381. cache: false
  382. asynchronous: false
  383. Connections {
  384. function onMinimap_image_changed() {
  385. Qt.callLater(function() {
  386. minimapImage.imageVersion++;
  387. });
  388. }
  389. target: game
  390. }
  391. Label {
  392. anchors.centerIn: parent
  393. text: qsTr("MINIMAP")
  394. color: "#3f5362"
  395. font.pixelSize: 12
  396. font.bold: true
  397. visible: parent.status !== Image.Ready
  398. }
  399. }
  400. }
  401. }
  402. }