MainMenu.qml 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. import QtQuick 2.15
  2. import QtQuick.Controls 2.15
  3. import QtQuick.Layouts 1.3
  4. import QtQuick.Window 2.15
  5. import StandardOfIron 1.0
  6. Item {
  7. id: root
  8. property bool gameStarted: false
  9. signal openSkirmish()
  10. signal openCampaign()
  11. signal openObjectives()
  12. signal openSettings()
  13. signal loadSave()
  14. signal saveGame()
  15. signal exitRequested()
  16. anchors.fill: parent
  17. z: 10
  18. focus: true
  19. Keys.onPressed: function(event) {
  20. if (event.key === Qt.Key_Down) {
  21. var newIndex = container.selectedIndex + 1;
  22. while (newIndex < menuModel.count) {
  23. var m = menuModel.get(newIndex);
  24. if (!m.requiresGame || root.gameStarted) {
  25. container.selectedIndex = newIndex;
  26. break;
  27. }
  28. newIndex++;
  29. }
  30. event.accepted = true;
  31. } else if (event.key === Qt.Key_Up) {
  32. var newIndex = container.selectedIndex - 1;
  33. while (newIndex >= 0) {
  34. var m = menuModel.get(newIndex);
  35. if (!m.requiresGame || root.gameStarted) {
  36. container.selectedIndex = newIndex;
  37. break;
  38. }
  39. newIndex--;
  40. }
  41. event.accepted = true;
  42. } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
  43. var m = menuModel.get(container.selectedIndex);
  44. if (m.requiresGame && !root.gameStarted) {
  45. event.accepted = true;
  46. return ;
  47. }
  48. if (m.idStr === "skirmish")
  49. root.openSkirmish();
  50. else if (m.idStr === "campaign")
  51. root.openCampaign();
  52. else if (m.idStr === "objectives")
  53. root.openObjectives();
  54. else if (m.idStr === "save")
  55. root.saveGame();
  56. else if (m.idStr === "load")
  57. root.loadSave();
  58. else if (m.idStr === "settings")
  59. root.openSettings();
  60. else if (m.idStr === "exit")
  61. root.exitRequested();
  62. event.accepted = true;
  63. } else if (event.key === Qt.Key_Escape) {
  64. if (typeof mainWindow !== 'undefined' && mainWindow.menuVisible && mainWindow.gameStarted) {
  65. mainWindow.menuVisible = false;
  66. event.accepted = true;
  67. }
  68. }
  69. }
  70. Rectangle {
  71. anchors.fill: parent
  72. color: Theme.dim
  73. }
  74. Rectangle {
  75. id: container
  76. property int selectedIndex: 0
  77. width: Math.min(parent.width * 0.78, 1100)
  78. height: Math.min(parent.height * 0.78, 700)
  79. anchors.centerIn: parent
  80. radius: Theme.radiusPanel
  81. color: Theme.panelBase
  82. border.color: Theme.panelBr
  83. border.width: 1
  84. opacity: 0.98
  85. clip: true
  86. Item {
  87. id: contentArea
  88. anchors.top: parent.top
  89. anchors.left: parent.left
  90. anchors.right: parent.right
  91. anchors.bottom: footerRow.top
  92. anchors.margins: Theme.spacingXLarge
  93. GridLayout {
  94. id: grid
  95. anchors.fill: parent
  96. rowSpacing: Theme.spacingMedium
  97. columnSpacing: 18
  98. columns: parent.width > 900 ? 2 : 1
  99. ColumnLayout {
  100. Layout.preferredWidth: parent.width > 900 ? parent.width * 0.45 : parent.width
  101. spacing: Theme.spacingLarge
  102. ColumnLayout {
  103. spacing: Theme.spacingSmall
  104. Label {
  105. text: qsTr("STANDARD OF IRON")
  106. color: Theme.textMain
  107. font.pointSize: Theme.fontSizeHero
  108. font.bold: true
  109. horizontalAlignment: Text.AlignLeft
  110. Layout.fillWidth: true
  111. elide: Label.ElideRight
  112. }
  113. Label {
  114. text: qsTr("A tiny but ambitious RTS")
  115. color: Theme.textSub
  116. font.pointSize: Theme.fontSizeMedium
  117. horizontalAlignment: Text.AlignLeft
  118. Layout.fillWidth: true
  119. elide: Label.ElideRight
  120. }
  121. }
  122. ListModel {
  123. id: menuModel
  124. ListElement {
  125. idStr: "skirmish"
  126. title: QT_TR_NOOP("Play — Skirmish")
  127. subtitle: QT_TR_NOOP("Select a map and start")
  128. requiresGame: false
  129. }
  130. ListElement {
  131. idStr: "campaign"
  132. title: QT_TR_NOOP("Play — Campaign")
  133. subtitle: QT_TR_NOOP("Story missions and battles")
  134. requiresGame: false
  135. }
  136. ListElement {
  137. idStr: "objectives"
  138. title: QT_TR_NOOP("Objectives")
  139. subtitle: QT_TR_NOOP("View current mission objectives")
  140. requiresGame: true
  141. }
  142. ListElement {
  143. idStr: "save"
  144. title: QT_TR_NOOP("Save Game")
  145. subtitle: QT_TR_NOOP("Save your current progress")
  146. requiresGame: true
  147. }
  148. ListElement {
  149. idStr: "load"
  150. title: QT_TR_NOOP("Load Game")
  151. subtitle: QT_TR_NOOP("Resume a previous game")
  152. requiresGame: false
  153. }
  154. ListElement {
  155. idStr: "settings"
  156. title: QT_TR_NOOP("Settings")
  157. subtitle: QT_TR_NOOP("Adjust graphics & controls")
  158. requiresGame: false
  159. }
  160. ListElement {
  161. idStr: "exit"
  162. title: QT_TR_NOOP("Exit")
  163. subtitle: QT_TR_NOOP("Quit the game")
  164. requiresGame: false
  165. }
  166. }
  167. Repeater {
  168. model: menuModel
  169. delegate: Item {
  170. id: menuItem
  171. property int idx: index
  172. property bool itemEnabled: !model.requiresGame || root.gameStarted
  173. Layout.fillWidth: true
  174. Layout.preferredHeight: container.width > 900 ? 64 : 56
  175. Rectangle {
  176. anchors.fill: parent
  177. radius: Theme.radiusLarge
  178. clip: true
  179. color: container.selectedIndex === idx ? Theme.selectedBg : menuItemMouse.containsPress ? Theme.hoverBg : Qt.rgba(0, 0, 0, 0)
  180. border.width: 1
  181. border.color: container.selectedIndex === idx ? Theme.selectedBr : Theme.cardBorder
  182. opacity: itemEnabled ? 1 : 0.4
  183. RowLayout {
  184. anchors.fill: parent
  185. anchors.margins: Theme.spacingSmall
  186. spacing: Theme.spacingMedium
  187. Item {
  188. Layout.fillWidth: true
  189. Layout.preferredWidth: 1
  190. }
  191. ColumnLayout {
  192. Layout.fillWidth: true
  193. spacing: Theme.spacingTiny
  194. Text {
  195. text: qsTr(model.title)
  196. Layout.fillWidth: true
  197. elide: Text.ElideRight
  198. color: itemEnabled ? (container.selectedIndex === idx ? Theme.textMain : Theme.textBright) : Theme.textDim
  199. font.pointSize: Theme.fontSizeLarge
  200. font.bold: container.selectedIndex === idx
  201. }
  202. Text {
  203. text: qsTr(model.subtitle)
  204. Layout.fillWidth: true
  205. elide: Text.ElideRight
  206. color: itemEnabled ? (container.selectedIndex === idx ? Theme.accentBright : Theme.textSubLite) : Theme.textHint
  207. font.pointSize: Theme.fontSizeSmall
  208. }
  209. }
  210. Text {
  211. text: "›"
  212. font.pointSize: Theme.fontSizeTitle
  213. color: itemEnabled ? (container.selectedIndex === idx ? Theme.textMain : Theme.textHint) : Theme.textDim
  214. opacity: itemEnabled ? 1 : 0.3
  215. }
  216. }
  217. Behavior on color {
  218. ColorAnimation {
  219. duration: Theme.animNormal
  220. }
  221. }
  222. Behavior on border.color {
  223. ColorAnimation {
  224. duration: Theme.animNormal
  225. }
  226. }
  227. }
  228. MouseArea {
  229. id: menuItemMouse
  230. anchors.fill: parent
  231. hoverEnabled: true
  232. acceptedButtons: Qt.LeftButton
  233. cursorShape: itemEnabled ? Qt.PointingHandCursor : Qt.ForbiddenCursor
  234. onEntered: {
  235. if (itemEnabled)
  236. container.selectedIndex = idx;
  237. }
  238. onClicked: {
  239. if (!itemEnabled)
  240. return ;
  241. if (model.idStr === "skirmish")
  242. root.openSkirmish();
  243. else if (model.idStr === "campaign")
  244. root.openCampaign();
  245. else if (model.idStr === "objectives")
  246. root.openObjectives();
  247. else if (model.idStr === "save")
  248. root.saveGame();
  249. else if (model.idStr === "load")
  250. root.loadSave();
  251. else if (model.idStr === "settings")
  252. root.openSettings();
  253. else if (model.idStr === "exit")
  254. root.exitRequested();
  255. }
  256. }
  257. }
  258. }
  259. }
  260. Rectangle {
  261. color: Qt.rgba(0, 0, 0, 0)
  262. radius: Theme.radiusMedium
  263. Layout.preferredWidth: parent.width > 900 ? parent.width * 0.45 : parent.width
  264. ColumnLayout {
  265. anchors.fill: parent
  266. anchors.margins: Theme.spacingSmall
  267. spacing: Theme.spacingMedium
  268. Rectangle {
  269. id: promo
  270. color: Theme.cardBase
  271. radius: Theme.radiusLarge
  272. border.color: Theme.border
  273. border.width: 1
  274. Layout.preferredHeight: 260
  275. clip: true
  276. ColumnLayout {
  277. anchors.fill: parent
  278. anchors.margins: Theme.spacingMedium
  279. spacing: Theme.spacingSmall
  280. Label {
  281. text: qsTr("Featured")
  282. color: Theme.accent
  283. font.pointSize: Theme.fontSizeMedium
  284. Layout.fillWidth: true
  285. elide: Label.ElideRight
  286. }
  287. Label {
  288. text: qsTr("Skirmish Mode")
  289. color: Theme.textMain
  290. font.pointSize: Theme.fontSizeTitle
  291. font.bold: true
  292. Layout.fillWidth: true
  293. elide: Label.ElideRight
  294. }
  295. Text {
  296. text: qsTr("Pick a map, adjust your forces and jump into battle. Modern controls and responsive UI.")
  297. color: Theme.textSubLite
  298. wrapMode: Text.WordWrap
  299. maximumLineCount: 3
  300. elide: Text.ElideRight
  301. Layout.fillWidth: true
  302. }
  303. }
  304. }
  305. Rectangle {
  306. color: Theme.cardBase
  307. radius: Theme.radiusLarge
  308. border.color: Theme.border
  309. border.width: 1
  310. Layout.preferredHeight: 120
  311. clip: true
  312. ColumnLayout {
  313. anchors.fill: parent
  314. anchors.margins: Theme.spacingSmall
  315. spacing: Theme.spacingSmall
  316. Label {
  317. text: qsTr("Tips")
  318. color: Theme.accent
  319. font.pointSize: Theme.fontSizeMedium
  320. Layout.fillWidth: true
  321. elide: Label.ElideRight
  322. }
  323. Text {
  324. text: qsTr("Hover menu items or use Up/Down and Enter to navigate. Play opens map selection.")
  325. color: Theme.textSubLite
  326. wrapMode: Text.WordWrap
  327. maximumLineCount: 3
  328. elide: Text.ElideRight
  329. Layout.fillWidth: true
  330. }
  331. }
  332. }
  333. }
  334. }
  335. }
  336. }
  337. RowLayout {
  338. id: footerRow
  339. anchors.left: parent.left
  340. anchors.right: parent.right
  341. anchors.bottom: parent.bottom
  342. anchors.margins: Theme.spacingXLarge
  343. spacing: Theme.spacingSmall
  344. Label {
  345. text: qsTr("v0.9 — prototype")
  346. color: Theme.textDim
  347. font.pointSize: Theme.fontSizeSmall
  348. }
  349. Item {
  350. Layout.fillWidth: true
  351. }
  352. Label {
  353. text: Qt.formatDateTime(new Date(), "yyyy-MM-dd")
  354. color: Theme.textHint
  355. font.pointSize: Theme.fontSizeSmall
  356. elide: Label.ElideRight
  357. }
  358. }
  359. }
  360. }