SaveGamePanel.qml 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. import QtQml 2.15
  2. import QtQuick 2.15
  3. import QtQuick.Controls 2.15
  4. import QtQuick.Layouts 1.3
  5. import StandardOfIron 1.0
  6. Item {
  7. id: root
  8. signal cancelled()
  9. signal saveRequested(string slotName)
  10. anchors.fill: parent
  11. z: 25
  12. onVisibleChanged: {
  13. if (!visible)
  14. return ;
  15. if (typeof saveListModel !== 'undefined')
  16. saveListModel.loadFromGame();
  17. if (typeof saveNameField !== 'undefined' && saveNameField)
  18. saveNameField.text = "Save_" + Qt.formatDateTime(new Date(), "yyyy-MM-dd_HH-mm");
  19. }
  20. Keys.onPressed: function(event) {
  21. if (event.key === Qt.Key_Escape) {
  22. root.cancelled();
  23. event.accepted = true;
  24. }
  25. }
  26. Component.onCompleted: {
  27. forceActiveFocus();
  28. }
  29. Connections {
  30. function onSave_slots_changed() {
  31. if (typeof saveListModel !== 'undefined')
  32. saveListModel.loadFromGame();
  33. }
  34. target: typeof game !== 'undefined' ? game : null
  35. }
  36. Rectangle {
  37. anchors.fill: parent
  38. color: Theme.dim
  39. }
  40. Rectangle {
  41. id: container
  42. width: Math.min(parent.width * 0.7, 900)
  43. height: Math.min(parent.height * 0.8, 600)
  44. anchors.centerIn: parent
  45. radius: Theme.radiusPanel
  46. color: Theme.panelBase
  47. border.color: Theme.panelBr
  48. border.width: 1
  49. opacity: 0.98
  50. ColumnLayout {
  51. anchors.fill: parent
  52. anchors.margins: Theme.spacingXLarge
  53. spacing: Theme.spacingLarge
  54. RowLayout {
  55. Layout.fillWidth: true
  56. spacing: Theme.spacingMedium
  57. Label {
  58. text: qsTr("Save Game")
  59. color: Theme.textMain
  60. font.pointSize: Theme.fontSizeHero
  61. font.bold: true
  62. Layout.fillWidth: true
  63. }
  64. StyledButton {
  65. text: qsTr("Cancel")
  66. buttonStyle: "secondary"
  67. onClicked: root.cancelled()
  68. }
  69. }
  70. Rectangle {
  71. Layout.fillWidth: true
  72. Layout.preferredHeight: 1
  73. color: Theme.border
  74. }
  75. RowLayout {
  76. Layout.fillWidth: true
  77. spacing: Theme.spacingMedium
  78. Label {
  79. text: qsTr("Save Name:")
  80. color: Theme.textSub
  81. font.pointSize: Theme.fontSizeMedium
  82. }
  83. TextField {
  84. id: saveNameField
  85. Layout.fillWidth: true
  86. placeholderText: qsTr("Enter save name...")
  87. text: "Save_" + Qt.formatDateTime(new Date(), "yyyy-MM-dd_HH-mm")
  88. font.pointSize: Theme.fontSizeMedium
  89. color: Theme.textMain
  90. background: Rectangle {
  91. color: Theme.cardBase
  92. border.color: saveNameField.activeFocus ? Theme.accent : Theme.border
  93. border.width: 1
  94. radius: Theme.radiusMedium
  95. }
  96. }
  97. StyledButton {
  98. text: qsTr("Save")
  99. enabled: saveNameField.text.length > 0
  100. onClicked: {
  101. if (saveListModel.slotExists(saveNameField.text)) {
  102. confirmOverwriteDialog.slotName = saveNameField.text;
  103. confirmOverwriteDialog.open();
  104. } else {
  105. root.saveRequested(saveNameField.text);
  106. }
  107. }
  108. }
  109. }
  110. Label {
  111. text: qsTr("Existing Saves")
  112. color: Theme.textSub
  113. font.pointSize: Theme.fontSizeMedium
  114. }
  115. Rectangle {
  116. Layout.fillWidth: true
  117. Layout.fillHeight: true
  118. color: Theme.cardBase
  119. border.color: Theme.border
  120. border.width: 1
  121. radius: Theme.radiusLarge
  122. ScrollView {
  123. anchors.fill: parent
  124. anchors.margins: Theme.spacingSmall
  125. clip: true
  126. ListView {
  127. id: saveListView
  128. spacing: Theme.spacingSmall
  129. model: ListModel {
  130. id: saveListModel
  131. function slotExists(name) {
  132. for (var i = 0; i < count; i++) {
  133. if (get(i).slotName === name)
  134. return true;
  135. }
  136. return false;
  137. }
  138. function loadFromGame() {
  139. clear();
  140. if (typeof game === 'undefined' || !game.get_save_slots)
  141. return ;
  142. var slots = game.get_save_slots();
  143. for (var i = 0; i < slots.length; i++) {
  144. append({
  145. "slotName": slots[i].slotName || slots[i].name,
  146. "title": slots[i].title || slots[i].name || slots[i].slotName || "Untitled Save",
  147. "timestamp": slots[i].timestamp,
  148. "map_name": slots[i].map_name || "Unknown Map",
  149. "thumbnail": slots[i].thumbnail || ""
  150. });
  151. }
  152. }
  153. Component.onCompleted: {
  154. loadFromGame();
  155. }
  156. }
  157. delegate: Rectangle {
  158. width: saveListView.width
  159. height: 80
  160. color: mouseArea.containsMouse ? Theme.hoverBg : Qt.rgba(0, 0, 0, 0)
  161. radius: Theme.radiusMedium
  162. border.color: Theme.cardBorder
  163. border.width: 1
  164. RowLayout {
  165. anchors.fill: parent
  166. anchors.margins: Theme.spacingMedium
  167. spacing: Theme.spacingMedium
  168. Rectangle {
  169. id: thumbnailContainer
  170. Layout.preferredWidth: 96
  171. Layout.preferredHeight: 64
  172. radius: Theme.radiusSmall
  173. color: Theme.cardBase
  174. border.color: Theme.cardBorder
  175. border.width: 1
  176. clip: true
  177. Image {
  178. id: thumbnailImage
  179. anchors.fill: parent
  180. anchors.margins: 2
  181. fillMode: Image.PreserveAspectCrop
  182. source: model.thumbnail && model.thumbnail.length > 0 ? "data:image/png;base64," + model.thumbnail : ""
  183. visible: source !== ""
  184. }
  185. Label {
  186. anchors.centerIn: parent
  187. visible: !thumbnailImage.visible
  188. text: qsTr("No Preview")
  189. color: Theme.textHint
  190. font.pointSize: Theme.fontSizeTiny
  191. }
  192. }
  193. ColumnLayout {
  194. Layout.fillWidth: true
  195. spacing: Theme.spacingTiny
  196. Label {
  197. text: model.title
  198. color: Theme.textMain
  199. font.pointSize: Theme.fontSizeLarge
  200. font.bold: true
  201. Layout.fillWidth: true
  202. elide: Label.ElideRight
  203. }
  204. Label {
  205. text: qsTr("Slot: %1").arg(model.slotName)
  206. color: Theme.textSub
  207. font.pointSize: Theme.fontSizeSmall
  208. Layout.fillWidth: true
  209. elide: Label.ElideRight
  210. }
  211. Label {
  212. text: model.map_name
  213. color: Theme.textSub
  214. font.pointSize: Theme.fontSizeSmall
  215. Layout.fillWidth: true
  216. elide: Label.ElideRight
  217. }
  218. Label {
  219. text: qsTr("Last saved: %1").arg(Qt.formatDateTime(new Date(model.timestamp), "yyyy-MM-dd hh:mm:ss"))
  220. color: Theme.textHint
  221. font.pointSize: Theme.fontSizeSmall
  222. Layout.fillWidth: true
  223. elide: Label.ElideRight
  224. }
  225. }
  226. StyledButton {
  227. text: qsTr("Overwrite")
  228. buttonStyle: "danger"
  229. implicitWidth: 100
  230. onClicked: {
  231. confirmOverwriteDialog.slotName = model.slotName;
  232. confirmOverwriteDialog.open();
  233. }
  234. }
  235. }
  236. MouseArea {
  237. id: mouseArea
  238. anchors.fill: parent
  239. hoverEnabled: true
  240. onClicked: {
  241. saveNameField.text = model.slotName;
  242. }
  243. }
  244. }
  245. }
  246. }
  247. }
  248. }
  249. }
  250. Dialog {
  251. id: confirmOverwriteDialog
  252. property string slotName: ""
  253. anchors.centerIn: parent
  254. width: Math.min(parent.width * 0.5, 400)
  255. title: qsTr("Confirm Overwrite")
  256. modal: true
  257. standardButtons: Dialog.Yes | Dialog.No
  258. onAccepted: {
  259. root.saveRequested(slotName);
  260. }
  261. contentItem: Rectangle {
  262. color: Theme.cardBase
  263. implicitHeight: warningText.implicitHeight + 40
  264. ColumnLayout {
  265. anchors.fill: parent
  266. anchors.margins: Theme.spacingMedium
  267. spacing: Theme.spacingMedium
  268. Label {
  269. id: warningText
  270. text: qsTr("Are you sure you want to overwrite the save:\n\"%1\"?").arg(confirmOverwriteDialog.slotName)
  271. color: Theme.textMain
  272. wrapMode: Text.WordWrap
  273. Layout.fillWidth: true
  274. font.pointSize: Theme.fontSizeMedium
  275. }
  276. }
  277. }
  278. }
  279. }