MaterialsSyncDialog.qml 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797
  1. //Copyright (c) 2022 Ultimaker B.V.
  2. //Cura is released under the terms of the LGPLv3 or higher.
  3. import QtQuick 2.15
  4. import QtQuick.Controls 2.15
  5. import QtQuick.Dialogs
  6. import QtQuick.Layouts 1.15
  7. import QtQuick.Window 2.1
  8. import Cura 1.1 as Cura
  9. import UM 1.6 as UM
  10. UM.Window
  11. {
  12. id: materialsSyncDialog
  13. property variant catalog: UM.I18nCatalog { name: "cura" }
  14. title: catalog.i18nc("@title:window", "Sync materials with printers")
  15. minimumWidth: UM.Theme.getSize("modal_window_minimum").width
  16. minimumHeight: UM.Theme.getSize("modal_window_minimum").height
  17. width: minimumWidth
  18. height: minimumHeight
  19. modality: Qt.ApplicationModal
  20. color: UM.Theme.getColor("main_background")
  21. property variant syncModel
  22. property alias pageIndex: swipeView.currentIndex
  23. property alias syncStatusText: syncStatusLabel.text
  24. property bool hasExportedUsb: false
  25. SwipeView
  26. {
  27. id: swipeView
  28. anchors.fill: parent
  29. interactive: false
  30. Item
  31. {
  32. id: introPage
  33. ColumnLayout
  34. {
  35. spacing: UM.Theme.getSize("default_margin").height
  36. anchors.fill: parent
  37. anchors.margins: UM.Theme.getSize("default_margin").width
  38. UM.Label
  39. {
  40. text: catalog.i18nc("@title:header", "Sync materials with printers")
  41. font: UM.Theme.getFont("large_bold")
  42. Layout.fillWidth: true
  43. }
  44. UM.Label
  45. {
  46. text: catalog.i18nc("@text", "Following a few simple steps, you will be able to synchronize all your material profiles with your printers.")
  47. font: UM.Theme.getFont("medium")
  48. Layout.fillWidth: true
  49. }
  50. Image
  51. {
  52. Layout.fillWidth: true
  53. Layout.fillHeight: true
  54. source: UM.Theme.getImage("material_ecosystem")
  55. fillMode: Image.PreserveAspectFit
  56. sourceSize.width: width
  57. }
  58. Item
  59. {
  60. Layout.preferredHeight: childrenRect.height
  61. Layout.alignment: Qt.AlignBottom
  62. Layout.fillWidth: true
  63. Cura.TertiaryButton
  64. {
  65. text: catalog.i18nc("@button", "Why do I need to sync material profiles?")
  66. iconSource: UM.Theme.getIcon("LinkExternal")
  67. isIconOnRightSide: true
  68. onClicked: Qt.openUrlExternally("https://support.ultimaker.com/hc/en-us/articles/360013137919?utm_source=cura&utm_medium=software&utm_campaign=sync-material-printer-why")
  69. }
  70. Cura.PrimaryButton
  71. {
  72. anchors.right: parent.right
  73. text: catalog.i18nc("@button", "Start")
  74. onClicked:
  75. {
  76. if(Cura.API.account.isLoggedIn)
  77. {
  78. if(Cura.API.account.permissions.includes("digital-factory.printer.write"))
  79. {
  80. swipeView.currentIndex += 2; //Skip sign in page. Continue to sync via cloud.
  81. }
  82. else
  83. {
  84. //Logged in, but no permissions to start syncing. Direct them to USB.
  85. swipeView.currentIndex = removableDriveSyncPage.SwipeView.index;
  86. }
  87. }
  88. else
  89. {
  90. swipeView.currentIndex += 1;
  91. }
  92. }
  93. }
  94. }
  95. }
  96. }
  97. Item
  98. {
  99. id: signinPage
  100. // While this page is active, continue to the next page if the user logs in.
  101. Connections
  102. {
  103. target: Cura.API.account
  104. function onLoginStateChanged(is_logged_in)
  105. {
  106. if(is_logged_in && signinPage.SwipeView.isCurrentItem)
  107. {
  108. if(Cura.API.account.permissions.includes("digital-factory.printer.write"))
  109. {
  110. swipeView.currentIndex += 1;
  111. }
  112. else
  113. {
  114. //Logged in, but no permissions to start syncing. Direct them to USB.
  115. swipeView.currentIndex = removableDriveSyncPage.SwipeView.index;
  116. }
  117. }
  118. }
  119. }
  120. ColumnLayout
  121. {
  122. spacing: UM.Theme.getSize("default_margin").height
  123. anchors.fill: parent
  124. anchors.margins: UM.Theme.getSize("default_margin").width
  125. UM.Label
  126. {
  127. text: catalog.i18nc("@title:header", "Sign in")
  128. font: UM.Theme.getFont("large_bold")
  129. Layout.fillWidth: true
  130. }
  131. UM.Label
  132. {
  133. text: catalog.i18nc("@text", "To automatically sync the material profiles with all your printers connected to Digital Factory you need to be signed in in Cura.")
  134. font: UM.Theme.getFont("medium")
  135. Layout.fillWidth: true
  136. }
  137. Image
  138. {
  139. Layout.alignment: Qt.AlignCenter
  140. Layout.preferredWidth: parent.width / 2
  141. source: UM.Theme.getImage("first_run_ultimaker_cloud")
  142. Layout.fillHeight: true
  143. sourceSize.width: width
  144. fillMode: Image.PreserveAspectFit
  145. }
  146. Item
  147. {
  148. Layout.preferredHeight: childrenRect.height
  149. Layout.alignment: Qt.AlignBottom
  150. Layout.fillWidth: true
  151. Cura.SecondaryButton
  152. {
  153. anchors.left: parent.left
  154. text: catalog.i18nc("@button", "Sync materials with USB")
  155. onClicked: swipeView.currentIndex = removableDriveSyncPage.SwipeView.index
  156. }
  157. Cura.PrimaryButton
  158. {
  159. anchors.right: parent.right
  160. text: catalog.i18nc("@button", "Sign in")
  161. onClicked: Cura.API.account.login()
  162. }
  163. }
  164. }
  165. }
  166. Item
  167. {
  168. id: printerListPage
  169. ColumnLayout
  170. {
  171. spacing: UM.Theme.getSize("default_margin").height
  172. anchors.fill: parent
  173. anchors.margins: UM.Theme.getSize("default_margin").width
  174. visible: cloudPrinterList.count > 0
  175. Row
  176. {
  177. spacing: UM.Theme.getSize("default_margin").width
  178. states: [
  179. State
  180. {
  181. name: "idle"
  182. when: typeof syncModel === "undefined" || syncModel.exportUploadStatus == "idle" || syncModel.exportUploadStatus == "uploading"
  183. PropertyChanges { target: printerListHeader; text: catalog.i18nc("@title:header", "The following printers will receive the new material profiles:") }
  184. PropertyChanges { target: printerListHeaderIcon; status: UM.StatusIcon.Status.NEUTRAL; width: 0 }
  185. },
  186. State
  187. {
  188. name: "error"
  189. when: typeof syncModel !== "undefined" && syncModel.exportUploadStatus == "error"
  190. PropertyChanges { target: printerListHeader; text: catalog.i18nc("@title:header", "Something went wrong when sending the materials to the printers.") }
  191. PropertyChanges { target: printerListHeaderIcon; status: UM.StatusIcon.Status.ERROR }
  192. },
  193. State
  194. {
  195. name: "success"
  196. when: typeof syncModel !== "undefined" && syncModel.exportUploadStatus == "success"
  197. PropertyChanges { target: printerListHeader; text: catalog.i18nc("@title:header", "Material profiles successfully synced with the following printers:") }
  198. PropertyChanges { target: printerListHeaderIcon; status: UM.StatusIcon.Status.POSITIVE }
  199. }
  200. ]
  201. UM.StatusIcon
  202. {
  203. id: printerListHeaderIcon
  204. width: UM.Theme.getSize("section_icon").width
  205. height: UM.Theme.getSize("section_icon").height
  206. anchors.verticalCenter: parent.verticalCenter
  207. }
  208. UM.Label
  209. {
  210. id: printerListHeader
  211. anchors.verticalCenter: parent.verticalCenter
  212. //Text is always defined by the states above.
  213. font: UM.Theme.getFont("large_bold")
  214. }
  215. }
  216. Row
  217. {
  218. Layout.fillWidth: true
  219. Layout.preferredHeight: childrenRect.height
  220. UM.Label
  221. {
  222. id: syncStatusLabel
  223. anchors.left: parent.left
  224. elide: Text.ElideRight
  225. visible: text !== ""
  226. font: UM.Theme.getFont("medium")
  227. }
  228. Cura.TertiaryButton
  229. {
  230. id: troubleshootingLink
  231. anchors.right: parent.right
  232. text: catalog.i18nc("@button", "Troubleshooting")
  233. visible: typeof syncModel !== "undefined" && syncModel.exportUploadStatus == "error"
  234. iconSource: UM.Theme.getIcon("LinkExternal")
  235. onClicked: Qt.openUrlExternally("https://support.ultimaker.com/hc/en-us/articles/360012019239?utm_source=cura&utm_medium=software&utm_campaign=sync-material-wizard-troubleshoot-cloud-printer")
  236. }
  237. }
  238. ListView
  239. {
  240. id: printerList
  241. Layout.fillWidth: true
  242. Layout.fillHeight: true
  243. clip: true
  244. ScrollBar.vertical: UM.ScrollBar
  245. {
  246. id: printerListScrollBar
  247. }
  248. spacing: UM.Theme.getSize("default_margin").height
  249. model: cloudPrinterList
  250. delegate: Rectangle
  251. {
  252. id: delegateContainer
  253. color: "transparent"
  254. border.color: UM.Theme.getColor("lining")
  255. border.width: UM.Theme.getSize("default_lining").width
  256. width: printerList.width - printerListScrollBar.width
  257. height: UM.Theme.getSize("machine_selector_icon").height + 2 * UM.Theme.getSize("default_margin").height
  258. property string syncStatus:
  259. {
  260. var printer_id = model.metadata["host_guid"]
  261. if(syncModel.printerStatus[printer_id] === undefined) //No status information available. Could be added after we started syncing.
  262. {
  263. return "idle";
  264. }
  265. return syncModel.printerStatus[printer_id];
  266. }
  267. Cura.IconWithText
  268. {
  269. anchors
  270. {
  271. verticalCenter: parent.verticalCenter
  272. left: parent.left
  273. leftMargin: Math.round(parent.height - height) / 2 //Equal margin on the left as above and below.
  274. right: parent.right
  275. rightMargin: Math.round(parent.height - height) / 2
  276. }
  277. text: model.name
  278. font: UM.Theme.getFont("medium")
  279. source: UM.Theme.getIcon("Printer", "medium")
  280. iconColor: UM.Theme.getColor("machine_selector_printer_icon")
  281. iconSize: UM.Theme.getSize("machine_selector_icon").width
  282. //Printer status badge (always cloud, but whether it's online or offline).
  283. UM.ColorImage
  284. {
  285. width: UM.Theme.getSize("printer_status_icon").width
  286. height: UM.Theme.getSize("printer_status_icon").height
  287. anchors
  288. {
  289. bottom: parent.bottom
  290. bottomMargin: -Math.round(height / 6)
  291. left: parent.left
  292. leftMargin: parent.iconSize - Math.round(width * 5 / 6)
  293. }
  294. source: UM.Theme.getIcon("CloudBadge", "low")
  295. color: UM.Theme.getColor("primary")
  296. //Make a themeable circle in the background so we can change it in other themes.
  297. Rectangle
  298. {
  299. anchors.centerIn: parent
  300. width: parent.width - 1.5 //1.5 pixels smaller (at least sqrt(2), regardless of pixel scale) so that the circle doesn't show up behind the icon due to anti-aliasing.
  301. height: parent.height - 1.5
  302. radius: width / 2
  303. color: UM.Theme.getColor("connection_badge_background")
  304. z: parent.z - 1
  305. }
  306. }
  307. }
  308. UM.ColorImage
  309. {
  310. id: printerSpinner
  311. width: UM.Theme.getSize("section_icon").width
  312. height: width
  313. anchors.verticalCenter: parent.verticalCenter
  314. anchors.right: parent.right
  315. anchors.rightMargin: Math.round((parent.height - height) / 2) //Same margin on the right as above and below.
  316. visible: delegateContainer.syncStatus === "uploading"
  317. source: UM.Theme.getIcon("ArrowDoubleCircleRight")
  318. color: UM.Theme.getColor("primary")
  319. RotationAnimator
  320. {
  321. target: printerSpinner
  322. from: 0
  323. to: 360
  324. duration: 1000
  325. loops: Animation.Infinite
  326. running: true
  327. }
  328. }
  329. UM.StatusIcon
  330. {
  331. width: UM.Theme.getSize("section_icon").width
  332. height: width
  333. anchors.verticalCenter: parent.verticalCenter
  334. anchors.right: parent.right
  335. anchors.rightMargin: Math.round((parent.height - height) / 2) //Same margin on the right as above and below.
  336. visible: delegateContainer.syncStatus === "failed" || delegateContainer.syncStatus === "success"
  337. status: delegateContainer.syncStatus === "success" ? UM.StatusIcon.Status.POSITIVE : UM.StatusIcon.Status.ERROR
  338. }
  339. }
  340. footer: Item
  341. {
  342. width: printerList.width - printerListScrollBar.width
  343. height: childrenRect.height + UM.Theme.getSize("default_margin").height
  344. visible: includeOfflinePrinterList.count - cloudPrinterList.count > 0 && typeof syncModel !== "undefined" && syncModel.exportUploadStatus === "idle"
  345. Rectangle
  346. {
  347. anchors.top: parent.top
  348. anchors.left: parent.left
  349. anchors.right: parent.right
  350. border.color: UM.Theme.getColor("lining")
  351. border.width: UM.Theme.getSize("default_lining").width
  352. anchors.topMargin: UM.Theme.getSize("default_margin").height
  353. height: childrenRect.height + 2 * UM.Theme.getSize("thick_margin").height
  354. color: "transparent"
  355. GridLayout
  356. {
  357. columns: 3
  358. rows: 2
  359. anchors.top: parent.top
  360. anchors.left: parent.left
  361. anchors.right: parent.right
  362. anchors.leftMargin: UM.Theme.getSize("thick_margin").width
  363. anchors.rightMargin: UM.Theme.getSize("thick_margin").width
  364. anchors.topMargin: UM.Theme.getSize("thick_margin").height
  365. anchors.bottomMargin: UM.Theme.getSize("thick_margin").height
  366. columnSpacing: UM.Theme.getSize("default_margin").width
  367. rowSpacing: UM.Theme.getSize("default_margin").height
  368. UM.StatusIcon
  369. {
  370. Layout.preferredWidth: UM.Theme.getSize("section_icon").width
  371. Layout.preferredHeight: UM.Theme.getSize("section_icon").height
  372. status: UM.StatusIcon.Status.WARNING
  373. }
  374. UM.Label
  375. {
  376. Layout.fillWidth: true
  377. Layout.alignment: Qt.AlignVCenter
  378. text: catalog.i18nc("@text Asking the user whether printers are missing in a list.", "Printers missing?")
  379. + "\n"
  380. + catalog.i18nc("@text", "Make sure all your printers are turned ON and connected to Digital Factory.")
  381. font: UM.Theme.getFont("medium")
  382. elide: Text.ElideRight
  383. }
  384. Cura.SecondaryButton
  385. {
  386. id: refreshListButton
  387. Layout.alignment: Qt.AlignVCenter
  388. readonly property int _AccountSyncState_SYNCING: 0
  389. visible: Cura.API.account.syncState != _AccountSyncState_SYNCING
  390. enabled: visible
  391. text: catalog.i18nc("@button", "Refresh List")
  392. iconSource: UM.Theme.getIcon("ArrowDoubleCircleRight")
  393. onClicked: Cura.API.account.sync(true)
  394. }
  395. Item
  396. {
  397. width: childrenRect.width
  398. Layout.alignment: Qt.AlignVCenter
  399. height: refreshListButton.height
  400. visible: !refreshListButton.visible
  401. UM.ColorImage
  402. {
  403. id: refreshingIcon
  404. height: UM.Theme.getSize("action_button_icon").height
  405. width: height
  406. anchors.verticalCenter: refreshingLabel.verticalCenter
  407. source: UM.Theme.getIcon("ArrowDoubleCircleRight")
  408. color: UM.Theme.getColor("primary")
  409. RotationAnimator
  410. {
  411. target: refreshingIcon
  412. from: 0
  413. to: 360
  414. duration: 1000
  415. loops: Animation.Infinite
  416. running: true
  417. }
  418. }
  419. UM.Label
  420. {
  421. id: refreshingLabel
  422. anchors.left: refreshingIcon.right
  423. anchors.leftMargin: UM.Theme.getSize("narrow_margin").width
  424. text: catalog.i18nc("@button", "Refreshing...")
  425. color: UM.Theme.getColor("primary")
  426. font: UM.Theme.getFont("medium")
  427. }
  428. }
  429. Cura.TertiaryButton
  430. {
  431. id: printerListTroubleshooting
  432. Layout.column: 1
  433. Layout.row: 1
  434. Layout.fillWidth: true
  435. leftPadding: 0
  436. text: catalog.i18nc("@button", "Troubleshooting")
  437. iconSource: UM.Theme.getIcon("LinkExternal")
  438. onClicked: Qt.openUrlExternally("https://support.ultimaker.com/hc/en-us/articles/360012019239?utm_source=cura&utm_medium=software&utm_campaign=sync-material-wizard-troubleshoot-cloud-printer")
  439. }
  440. }
  441. }
  442. }
  443. }
  444. Item
  445. {
  446. Layout.fillWidth: true
  447. Layout.preferredHeight: childrenRect.height
  448. Layout.alignment: Qt.AlignBottom
  449. Cura.SecondaryButton
  450. {
  451. anchors.left: parent.left
  452. text: catalog.i18nc("@button", "Sync materials with USB")
  453. onClicked: swipeView.currentIndex = removableDriveSyncPage.SwipeView.index
  454. }
  455. Cura.PrimaryButton
  456. {
  457. id: syncButton
  458. anchors.right: parent.right
  459. text:
  460. {
  461. if(typeof syncModel !== "undefined" && syncModel.exportUploadStatus == "error")
  462. {
  463. return catalog.i18nc("@button", "Try again");
  464. }
  465. if(typeof syncModel !== "undefined" && syncModel.exportUploadStatus == "success")
  466. {
  467. return catalog.i18nc("@button", "Done");
  468. }
  469. return catalog.i18nc("@button", "Sync");
  470. }
  471. onClicked:
  472. {
  473. if(typeof syncModel !== "undefined" && syncModel.exportUploadStatus == "success")
  474. {
  475. materialsSyncDialog.close();
  476. }
  477. else
  478. {
  479. syncModel.exportUpload();
  480. }
  481. }
  482. visible:
  483. {
  484. if(!syncModel) //When the dialog is created, this is not set yet.
  485. {
  486. return true;
  487. }
  488. return syncModel.exportUploadStatus != "uploading";
  489. }
  490. }
  491. Item
  492. {
  493. anchors.right: parent.right
  494. width: childrenRect.width
  495. height: syncButton.height
  496. visible: !syncButton.visible
  497. UM.ColorImage
  498. {
  499. id: syncingIcon
  500. height: UM.Theme.getSize("action_button_icon").height
  501. width: height
  502. anchors.verticalCenter: syncingLabel.verticalCenter
  503. source: UM.Theme.getIcon("ArrowDoubleCircleRight")
  504. color: UM.Theme.getColor("primary")
  505. RotationAnimator
  506. {
  507. target: syncingIcon
  508. from: 0
  509. to: 360
  510. duration: 1000
  511. loops: Animation.Infinite
  512. running: true
  513. }
  514. }
  515. UM.Label
  516. {
  517. id: syncingLabel
  518. anchors.left: syncingIcon.right
  519. anchors.leftMargin: UM.Theme.getSize("narrow_margin").width
  520. text: catalog.i18nc("@button", "Syncing")
  521. color: UM.Theme.getColor("primary")
  522. font: UM.Theme.getFont("medium")
  523. }
  524. }
  525. }
  526. }
  527. // Placeholder for when the user has no cloud printers.
  528. ColumnLayout
  529. {
  530. spacing: UM.Theme.getSize("default_margin").height
  531. anchors.fill: parent
  532. anchors.margins: UM.Theme.getSize("default_margin").width
  533. visible: cloudPrinterList.count == 0
  534. UM.Label
  535. {
  536. text: catalog.i18nc("@title:header", "No printers found")
  537. font: UM.Theme.getFont("large_bold")
  538. Layout.fillWidth: true
  539. }
  540. Item
  541. {
  542. Layout.fillWidth: true
  543. Layout.fillHeight: true
  544. Image
  545. {
  546. anchors.fill: parent
  547. source: UM.Theme.getImage("3d_printer_faded")
  548. sourceSize.width: width
  549. fillMode: Image.PreserveAspectFit
  550. }
  551. }
  552. UM.Label
  553. {
  554. text: catalog.i18nc("@text", "It seems like you don't have any compatible printers connected to Digital Factory. Make sure your printer is connected and it's running the latest firmware.")
  555. Layout.fillWidth: true
  556. horizontalAlignment: Text.AlignHCenter
  557. }
  558. Item
  559. {
  560. Layout.fillWidth: true
  561. Layout.preferredHeight: parent.height / 4
  562. Cura.TertiaryButton
  563. {
  564. text: catalog.i18nc("@button", "Learn how to connect your printer to Digital Factory")
  565. iconSource: UM.Theme.getIcon("LinkExternal")
  566. onClicked: Qt.openUrlExternally("https://support.ultimaker.com/hc/en-us/articles/360012019239?utm_source=cura&utm_medium=software&utm_campaign=sync-material-wizard-add-cloud-printer")
  567. anchors.horizontalCenter: parent.horizontalCenter
  568. maximumWidth: parent.width
  569. }
  570. }
  571. Item
  572. {
  573. Layout.preferredHeight: childrenRect.height
  574. Layout.alignment: Qt.AlignBottom
  575. Layout.fillWidth: true
  576. Cura.SecondaryButton
  577. {
  578. anchors.left: parent.left
  579. text: catalog.i18nc("@button", "Sync materials with USB")
  580. onClicked: swipeView.currentIndex = removableDriveSyncPage.SwipeView.index
  581. }
  582. RowLayout
  583. {
  584. anchors.right: parent.right
  585. spacing: UM.Theme.getSize("default_margin").width
  586. Cura.SecondaryButton
  587. {
  588. text: catalog.i18nc("@button", "Refresh")
  589. iconSource: UM.Theme.getIcon("ArrowDoubleCircleRight")
  590. outlineColor: "transparent"
  591. onClicked: Cura.API.account.sync(true)
  592. }
  593. Cura.PrimaryButton
  594. {
  595. id: disabledSyncButton
  596. text: catalog.i18nc("@button", "Sync")
  597. enabled: false // If there are no printers, always disable this button.
  598. }
  599. }
  600. }
  601. }
  602. }
  603. Item
  604. {
  605. id: removableDriveSyncPage
  606. ColumnLayout
  607. {
  608. spacing: UM.Theme.getSize("default_margin").height
  609. anchors.fill: parent
  610. anchors.margins: UM.Theme.getSize("default_margin").width
  611. UM.Label
  612. {
  613. text: catalog.i18nc("@title:header", "Sync material profiles via USB")
  614. font: UM.Theme.getFont("large_bold")
  615. Layout.fillWidth: true
  616. }
  617. UM.Label
  618. {
  619. text: catalog.i18nc("@text In the UI this is followed by a list of steps the user needs to take.", "Follow the following steps to load the new material profiles to your printer.")
  620. font: UM.Theme.getFont("medium")
  621. Layout.fillWidth: true
  622. }
  623. RowLayout
  624. {
  625. Layout.fillWidth: true
  626. Layout.fillHeight: true
  627. spacing: UM.Theme.getSize("default_margin").width
  628. Item
  629. {
  630. Layout.preferredWidth: parent.width / 3
  631. Layout.fillHeight: true
  632. Image
  633. {
  634. anchors.fill: parent
  635. source: UM.Theme.getImage("insert_usb")
  636. verticalAlignment: Image.AlignVCenter
  637. horizontalAlignment: Image.AlignHCenter
  638. fillMode: Image.PreserveAspectFit
  639. sourceSize.width: width
  640. }
  641. }
  642. UM.Label
  643. {
  644. Layout.alignment: Qt.AlignCenter
  645. Layout.fillWidth: true
  646. text: "1. " + catalog.i18nc("@text", "Click the export material archive button.")
  647. + "\n2. " + catalog.i18nc("@text", "Save the .umm file on a USB stick.")
  648. + "\n3. " + catalog.i18nc("@text", "Insert the USB stick into your printer and launch the procedure to load new material profiles.")
  649. font: UM.Theme.getFont("medium")
  650. }
  651. }
  652. Cura.TertiaryButton
  653. {
  654. Layout.fillWidth: true
  655. text: catalog.i18nc("@button", "How to load new material profiles to my printer")
  656. iconSource: UM.Theme.getIcon("LinkExternal")
  657. onClicked: Qt.openUrlExternally("https://support.ultimaker.com/hc/en-us/articles/4403319801106/?utm_source=cura&utm_medium=software&utm_campaign=add-material-profiles-via-usb")
  658. }
  659. Item
  660. {
  661. Layout.preferredHeight: childrenRect.height
  662. Layout.alignment: Qt.AlignBottom
  663. Layout.fillWidth: true
  664. Cura.SecondaryButton
  665. {
  666. anchors.left: parent.left
  667. text: catalog.i18nc("@button", "Back")
  668. onClicked: swipeView.currentIndex = 0 //Reset to first page.
  669. }
  670. Cura.PrimaryButton
  671. {
  672. id: exportUsbButton
  673. anchors.right: parent.right
  674. property bool hasExported: false
  675. text: materialsSyncDialog.hasExportedUsb ? catalog.i18nc("@button", "Done") : catalog.i18nc("@button", "Export material archive")
  676. onClicked:
  677. {
  678. if(!materialsSyncDialog.hasExportedUsb)
  679. {
  680. exportUsbDialog.currentFolder = `${syncModel.getPreferredExportAllPath()}/materials.umm`;
  681. exportUsbDialog.open();
  682. }
  683. else
  684. {
  685. materialsSyncDialog.close();
  686. }
  687. }
  688. }
  689. }
  690. }
  691. }
  692. }
  693. property variant cloudPrinterList: Cura.GlobalStacksModel
  694. {
  695. filterConnectionType: 3 //Only show cloud connections.
  696. filterOnlineOnly: true //Only show printers that are online.
  697. filterCapabilities: ["import_material"] //Only show printers that can receive the material profiles.
  698. }
  699. property variant includeOfflinePrinterList: Cura.GlobalStacksModel
  700. {
  701. //In order to show a refresh button only when there are offline cloud printers, we need to know if there are any offline printers.
  702. //A global stacks model without the filter for online-only printers allows this.
  703. filterConnectionType: 3 //Still only show cloud connections.
  704. }
  705. property variant exportUsbDialog: FileDialog
  706. {
  707. title: catalog.i18nc("@title:window", "Export All Materials")
  708. nameFilters: ["Material archives (*.umm)", "All files (*)"]
  709. fileMode: FileDialog.SaveFile
  710. onAccepted:
  711. {
  712. syncModel.exportAll(selectedFile);
  713. CuraApplication.setDefaultPath("dialog_material_path", folder);
  714. materialsSyncDialog.hasExportedUsb = true;
  715. }
  716. }
  717. }