InstallerUi.qml 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. import QtQuick 2.0
  2. import QtQuick.Controls 2.15
  3. import QtQml.Models 2.15
  4. import "view"
  5. import "components"
  6. Item {
  7. anchors.fill: parent
  8. signal browseClicked()
  9. signal installClicked()
  10. signal updateClicked()
  11. signal preDownloadClicked()
  12. signal startClicked()
  13. signal retryClicked()
  14. signal cancelConfirmed()
  15. signal cancelRejected()
  16. signal repatchClicked()
  17. signal stopConfirmed()
  18. signal killWineServerClicked()
  19. signal checkSelfUpdateClicked()
  20. readonly property int uiStateLoading: 1
  21. readonly property int uiStateInstall: 2
  22. readonly property int uiStateInstalling: 3
  23. readonly property int uiStateUpdating: 5
  24. readonly property int uiStateStart: 6
  25. readonly property int uiStateRetry: 7
  26. readonly property int uiStatePatch: 8
  27. readonly property int uiStateRunning: 9
  28. readonly property int uiStateStarting: 10
  29. readonly property int uiStateStopping: 11
  30. readonly property int uiStateWrongDir: 12
  31. property bool hasUpdate: false
  32. property bool hasPreDownload: false
  33. property int uiState: uiStateLoading
  34. property alias isLogVisible: buttonLog.checked
  35. property alias progressText: textProgress.text
  36. property alias progress: progressBar.value // 0..1
  37. function log(message) {
  38. textLog.append(message)
  39. }
  40. function clearLog() {
  41. textLog.text = ""
  42. }
  43. function showLog() {
  44. buttonLog.checked = true
  45. }
  46. function confirmCancelInstall() {
  47. dialogCancelInstall.open()
  48. }
  49. Image {
  50. id: imageBackground
  51. anchors.fill: parent
  52. fillMode: Image.PreserveAspectCrop
  53. source: "qrc:/bg/bg.jpg"
  54. visible: !settings.hideBackground
  55. }
  56. Text {
  57. id: textVersion
  58. text: qsTr("Game Version:") + "\n" + (installedVersion || "?")
  59. anchors.right: parent.right
  60. anchors.top: parent.top
  61. anchors.topMargin: block * 0.75
  62. anchors.rightMargin: block
  63. horizontalAlignment: Text.AlignRight
  64. font.pixelSize: block / 1.5
  65. visible: !imageBackground.visible
  66. }
  67. Outline {
  68. id: versionOutline
  69. source: textVersion
  70. visible: imageBackground.visible
  71. }
  72. Rectangle {
  73. border.color: "black"
  74. radius: space / 4
  75. color: "#E0F0F0F0"
  76. anchors.left: parent.left
  77. anchors.right: parent.right
  78. anchors.top: parent.top
  79. anchors.bottom: buttonBar.top
  80. anchors.margins: space
  81. visible: isLogVisible
  82. Flickable {
  83. id: flickable
  84. anchors.fill: parent
  85. boundsBehavior: Flickable.DragAndOvershootBounds
  86. flickableDirection: Flickable.VerticalFlick
  87. ScrollBar.vertical: ScrollBar {
  88. id: flickScroll
  89. }
  90. TextArea.flickable: TextArea {
  91. id: textLog
  92. width: parent.width
  93. height: parent.height
  94. readOnly: true
  95. wrapMode: TextArea.Wrap
  96. persistentSelection: true
  97. bottomPadding: 0
  98. selectByMouse: true
  99. color: "black"
  100. }
  101. function append(text) {
  102. let value = textLog.text + text
  103. let endPos = flickable.contentHeight * (1.0 - flickable.visibleArea.heightRatio)
  104. let pos = flickable.contentY
  105. textLog.text = value
  106. if (pos === endPos) {
  107. flickable.contentY = endPos
  108. } else {
  109. flickable.contentY = pos
  110. }
  111. }
  112. }
  113. }
  114. Item {
  115. id: buttonBar
  116. height: block + space
  117. anchors.left: parent.left
  118. anchors.right: parent.right
  119. anchors.bottom: parent.bottom
  120. ImageButton {
  121. id: buttonSettings
  122. anchors.left: parent.left
  123. anchors.margins: space
  124. icon: "settings"
  125. enabled: uiState != uiStateInstalling && uiState != uiStateUpdating && !isPreDownloading
  126. onClicked: popupSettingsMenu.open()
  127. }
  128. ImageButton {
  129. id: buttonLog
  130. anchors.left: buttonSettings.right
  131. anchors.margins: space
  132. icon: "log"
  133. checkable: true
  134. checked: true
  135. }
  136. Text {
  137. id: textStatus
  138. visible: progressBar.visible
  139. anchors.left: textProgress.left
  140. anchors.right: textProgress.right
  141. anchors.bottom: textProgress.top
  142. anchors.bottomMargin: space / 4
  143. text: isPreDownloading ? qsTr("Pre-downloading...")
  144. : uiState == uiStateInstalling ? qsTr("Installing...")
  145. : uiState == uiStateUpdating ? qsTr("Updating...")
  146. : ""
  147. }
  148. Outline {
  149. id: textStatusOutline
  150. source: textStatus
  151. visible: textStatus.visible
  152. }
  153. Text {
  154. id: textProgress
  155. visible: progressBar.visible
  156. anchors.left: progressBar.left
  157. anchors.right: progressBar.right
  158. anchors.bottom: progressBar.top
  159. anchors.bottomMargin: space / 2
  160. elide: Text.ElideRight
  161. }
  162. Outline {
  163. id: textProgressOutline
  164. source: textProgress
  165. visible: textProgress.visible
  166. }
  167. ProgressBar {
  168. id: progressBar
  169. visible: isPreDownloading || uiState == uiStateInstalling || uiState == uiStateUpdating
  170. anchors.left: buttonLog.right
  171. anchors.right: buttonUpdate.visible ? buttonUpdate.left
  172. : buttonBrowse.visible ? buttonBrowse.left
  173. : buttonInstallUpdate.left
  174. anchors.bottom: parent.bottom
  175. anchors.margins: space
  176. }
  177. Button {
  178. id: buttonBrowse
  179. visible: uiState == uiStateInstall || uiState == uiStateWrongDir
  180. title: qsTr("Browse...")
  181. anchors.right: buttonInstallUpdate.left
  182. anchors.margins: space
  183. onClicked: browseClicked()
  184. }
  185. Button {
  186. id: buttonUpdate
  187. visible: hasUpdate && (uiState == uiStateStart || uiState == uiStatePatch) || hasPreDownload
  188. title: !hasPreDownload ? (qsTr("Update to") + " v" + availableVersion)
  189. : isPreDownloading ? qsTr("Stop\nPre-Download")
  190. : (qsTr("Pre-Download") + "\nv" + preDownloadVersion)
  191. anchors.right: buttonInstallUpdate.left
  192. anchors.margins: space
  193. onClicked: isPreDownloading ? cancelConfirmed()
  194. : hasPreDownload ? preDownloadClicked() : updateClicked()
  195. }
  196. Button {
  197. id: buttonInstallUpdate
  198. title: uiState == uiStateInstall ? qsTr("Install")
  199. : uiState == uiStateRetry ? qsTr("Retry")
  200. : uiState == uiStateLoading ? qsTr("Loading...")
  201. : uiState == uiStateInstalling ? qsTr("Cancel")
  202. : uiState == uiStateUpdating ? qsTr("Cancel")
  203. : uiState == uiStateStart ? (qsTr("Start") + (hasUpdate ? (" v" + localVersionLoader.version) : ""))
  204. : uiState == uiStatePatch ? qsTr("Patch")
  205. : uiState == uiStateRunning ? qsTr("Stop")
  206. : uiState == uiStateStarting ? qsTr("Starting...")
  207. : uiState == uiStateStopping ? qsTr("Stopping...")
  208. : uiState == uiStateWrongDir ? qsTr("Install")
  209. : ""
  210. anchors.right: parent.right
  211. anchors.margins: space
  212. enabled: uiState != uiStateLoading
  213. && uiState != uiStateStarting
  214. && uiState != uiStateStopping
  215. && uiState != uiStateWrongDir
  216. && (!isPreDownloading || (uiState != uiStateInstall && uiState != uiStatePatch))
  217. onClicked: uiState == uiStateInstall ? installClicked()
  218. : uiState == uiStateInstalling ? confirmCancelInstall()
  219. : uiState == uiStateUpdating ? confirmCancelInstall()
  220. : uiState == uiStateStart ? startClicked()
  221. : uiState == uiStateRetry ? retryClicked()
  222. : uiState == uiStatePatch ? repatchClicked()
  223. : uiState == uiStateRunning ? dialogStopClient.open()
  224. : undefined
  225. }
  226. }
  227. Dialog {
  228. id: dialogCancelInstall
  229. anchors.centerIn: parent
  230. title: window.title
  231. standardButtons: Dialog.Yes | Dialog.No
  232. modal: true
  233. focus: true
  234. Text {
  235. id: textDialog
  236. anchors.fill: parent
  237. text: qsTr("Some tasks are still running. Do you wish to stop them?")
  238. }
  239. onAccepted: cancelConfirmed()
  240. onRejected: cancelRejected()
  241. }
  242. Dialog {
  243. id: dialogStopClient
  244. anchors.centerIn: parent
  245. title: window.title
  246. standardButtons: Dialog.Yes | Dialog.No
  247. modal: true
  248. focus: true
  249. Text {
  250. anchors.fill: parent
  251. text: qsTr("Please use this only as emergency option.")
  252. + "\n" + qsTr("Do you wish to terminate client?")
  253. }
  254. onAccepted: stopConfirmed()
  255. }
  256. Dialog {
  257. id: dialogFirstRun
  258. anchors.centerIn: parent
  259. title: window.title
  260. standardButtons: Dialog.Ok
  261. modal: true
  262. focus: true
  263. Text {
  264. anchors.fill: parent
  265. text: qsTr("The game is not meant to run on Linux. It may also break at any time.\nThere's absolutely no guarantee.")
  266. }
  267. Component.onCompleted: {
  268. if (settings.firstRun) {
  269. dialogFirstRun.open()
  270. settings.firstRun = false
  271. }
  272. }
  273. }
  274. Dialog {
  275. id: dialogAbout
  276. anchors.centerIn: parent
  277. title: window.title
  278. standardButtons: Dialog.Ok
  279. modal: true
  280. focus: true
  281. Row {
  282. anchors.fill: parent
  283. spacing: space
  284. Image {
  285. id: aboutIcon
  286. source: "qrc:/appicon/paimon-launcher.png"
  287. }
  288. Text {
  289. textFormat: Text.RichText
  290. onLinkActivated: Qt.openUrlExternally(link)
  291. text: "Original workaround project: <a href='" + patchRepo + "'>Dawn</a><br/>"
  292. + "<a href='" + patchRepo + "/src/master/TROUBLESHOOTING.md'>Troubleshooting page</a><br/>"
  293. + "<a href='" + patchRepo + "/src/master/TWEAKS.md'>Game Tweaks</a><br/>"
  294. + "<br/><br/>"
  295. + "<a href='https://notabug.org/loentar/paimon-launcher/releases'>Check for new versions of launcher here</a><br/><br/>"
  296. + "For bug reports please visit <br/>"
  297. + "<a href='https://notabug.org/loentar/paimon-launcher/issues'>Paimon Launcher Issues</a><br/><br/>"
  298. + "Copyright © Loentar 2021"
  299. }
  300. }
  301. }
  302. Script {
  303. id: script
  304. function openWineConfig() {
  305. execScript('client-wrapper', 'wineconfig')
  306. }
  307. }
  308. Popup {
  309. id: popupSettingsMenu
  310. x: buttonSettings.x
  311. y: parent.height - buttonSettings.height - height - space
  312. width: block * 5
  313. height: block * listMenu.model.count
  314. padding: 0
  315. focus: true
  316. clip: true
  317. closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
  318. background: Rectangle {
  319. color: "lightgray"
  320. border.color: "black"
  321. radius: space / 4
  322. }
  323. ListView {
  324. id: listMenu
  325. interactive: false
  326. anchors.fill: parent
  327. model: ListModel {
  328. ListElement { name: "browse"; text: qsTr("Change Game location...") }
  329. ListElement { name: "hidebg"; text: qsTr("Hide background") }
  330. ListElement { name: "clearlog"; text: qsTr("Clear log") }
  331. ListElement { name: "speedup"; text: qsTr("Speedup downloads") }
  332. ListElement { name: "limitdl"; text: qsTr("Limit download speed") }
  333. ListElement { name: "repatch"; text: qsTr("Re-apply Wine patch") }
  334. ListElement { name: "killwine"; text: qsTr("Kill wineserver") }
  335. ListElement { name: "editwine"; text: qsTr("Edit wineconfig.sh") }
  336. ListElement { name: "selfupdate"; text: qsTr("Check for updates...") }
  337. ListElement { name: "about"; text: qsTr("About...") }
  338. }
  339. delegate: Button {
  340. checkBox: model.name === "speedup"
  341. || model.name === "limitdl"
  342. || model.name === "hidebg"
  343. checked: (model.name === "speedup" && settings.multiConnections) ||
  344. (model.name === "limitdl" && settings.limitDownload) ||
  345. (model.name === "hidebg" && settings.hideBackground)
  346. title: model.text
  347. enabled: model.name !== "repatch" || !!installedVersion
  348. width: listMenu.width
  349. border.width: 0
  350. color: "transparent"
  351. horizontalAlignment: Text.AlignLeft
  352. onClicked: {
  353. popupSettingsMenu.close()
  354. switch (model.name) {
  355. case "browse":
  356. browseClicked()
  357. break
  358. case "hidebg":
  359. settings.hideBackground = !settings.hideBackground
  360. break
  361. case "clearlog":
  362. clearLog()
  363. break
  364. case "limitdl":
  365. settings.limitDownload = !settings.limitDownload
  366. break
  367. case "speedup":
  368. settings.multiConnections = !settings.multiConnections
  369. break
  370. case "repatch":
  371. repatchClicked()
  372. break
  373. case "killwine":
  374. killWineServerClicked()
  375. break
  376. case "editwine":
  377. script.openWineConfig()
  378. break
  379. case "selfupdate":
  380. checkSelfUpdateClicked()
  381. break
  382. case "about":
  383. dialogAbout.open()
  384. break
  385. }
  386. }
  387. }
  388. }
  389. }
  390. }