@@ -9,10 +9,41 @@ import Controls 1.0
99Page {
1010 id: root
1111
12+ property bool isLoading: false
13+
14+ // Schedule an action to run after the current frame has been rendered and
15+ // displayed, so that visual feedback (highlight, spinner) is visible before
16+ // the UI thread is blocked by a heavy synchronous operation.
17+ // Uses _window (the ApplicationWindow id from main.qml) since root.window
18+ // is not reliably available for Page items inside a StackView.
19+ function executeAfterFrameRendered (action , params = undefined ) {
20+ function onFrame () {
21+ _window .frameSwapped .disconnect (onFrame)
22+ if (params === undefined )
23+ action ()
24+ else
25+ action (params)
26+ }
27+ _window .frameSwapped .connect (onFrame)
28+ }
29+
30+ function openProject (path ) {
31+ mainStack .push (" Application.qml" )
32+ if (_currentScene .load (path)) {
33+ MeshroomApp .addRecentProjectFile (path)
34+ } else {
35+ root .isLoading = false
36+ homepageGridView .loadingIndex = - 1
37+ }
38+ }
39+
1240 onVisibleChanged: {
1341 logo .playing = false
1442 if (visible) {
1543 logo .playing = true
44+ isLoading = false
45+ homepageGridView .loadingIndex = - 1
46+ pipelinesListView .loadingIndex = - 1
1647 }
1748 }
1849
@@ -242,6 +273,8 @@ Page {
242273 id: pipelinesListView
243274 visible: tabPanel .currentTab === 0
244275
276+ property int loadingIndex: - 1
277+
245278 anchors .fill : parent
246279 anchors .margins : 10
247280
@@ -251,20 +284,44 @@ Page {
251284 id: pipelineDelegate
252285 padding: 10
253286 width: pipelinesListView .width
287+ enabled: ! root .isLoading || index === pipelinesListView .loadingIndex
288+ opacity: (! root .isLoading || index === pipelinesListView .loadingIndex ) ? 1.0 : 0.4
289+
290+ contentItem: RowLayout {
291+ Label {
292+ id: pipeline
293+ Layout .fillWidth : true
294+ horizontalAlignment: Text .AlignLeft
295+ verticalAlignment: Text .AlignVCenter
296+ text: modelData[" name" ]
297+ }
298+ BusyIndicator {
299+ Layout .preferredWidth : 24
300+ Layout .preferredHeight : 24
301+ running: index === pipelinesListView .loadingIndex && root .isLoading
302+ visible: running
303+ }
304+ }
254305
255- contentItem: Label {
256- id: pipeline
257- horizontalAlignment: Text .AlignLeft
258- verticalAlignment: Text .AlignVCenter
259- text: modelData[" name" ]
306+ // Highlight overlay shown when this pipeline is being loaded
307+ Rectangle {
308+ anchors .fill : parent
309+ color: " transparent"
310+ border .color : palette .highlight
311+ border .width : 2
312+ visible: root .isLoading && index === pipelinesListView .loadingIndex
260313 }
261314
262315 Connections {
263316 target: pipelineDelegate
264317 function onClicked () {
265- // Open pipeline
266- mainStack .push (" Application.qml" )
267- _currentScene .new (modelData[" path" ])
318+ root .isLoading = true
319+ pipelinesListView .loadingIndex = index
320+ let path = modelData[" path" ]
321+ root .executeAfterFrameRendered (function () {
322+ mainStack .push (" Application.qml" )
323+ _currentScene .new (path)
324+ })
268325 }
269326 }
270327 }
@@ -276,6 +333,8 @@ Page {
276333 anchors .fill : parent
277334 anchors .topMargin : cellHeight * 0.1
278335
336+ property int loadingIndex: - 1
337+
279338 cellWidth: 195
280339 cellHeight: cellWidth
281340 anchors .margins : 10
@@ -311,6 +370,7 @@ Page {
311370
312371 width: homepageGridView .cellWidth
313372 height: homepageGridView .cellHeight
373+ opacity: (! root .isLoading || index === homepageGridView .loadingIndex ) ? 1.0 : 0.4
314374
315375 property var source: modelData[" thumbnail" ] ? Filepath .stringToUrl (modelData[" thumbnail" ]) : " "
316376 property int retryCount: 0
@@ -387,6 +447,8 @@ Page {
387447
388448 onClicked : function (mouse ) {
389449
450+ if (root .isLoading ) return
451+
390452 if (mouse .button === Qt .RightButton ) {
391453
392454 if (! modelData[" path" ]) { return }
@@ -403,11 +465,10 @@ Page {
403465 }
404466
405467 else {
406- // Open project
407- mainStack .push (" Application.qml" )
408- if (_currentScene .load (modelData[" path" ])) {
409- MeshroomApp .addRecentProjectFile (modelData[" path" ])
410- }
468+ root .isLoading = true
469+ homepageGridView .loadingIndex = index
470+ let path = modelData[" path" ]
471+ root .executeAfterFrameRendered (openProject, path)
411472 }
412473 }
413474 }
@@ -416,13 +477,13 @@ Page {
416477 id: projectContextMenu
417478
418479 MenuItem {
419- enabled: projectDelegate .fileExists
480+ enabled: projectDelegate .fileExists && ! root . isLoading
420481 text: " Open"
421- onTriggered: {
422- if ( _currentScene . load (modelData[ " path " ])) {
423- mainStack . push ( " Application.qml " )
424- MeshroomApp . addRecentProjectFile ( modelData[" path" ])
425- }
482+ onTriggered: {
483+ root . isLoading = true
484+ homepageGridView . loadingIndex = index
485+ let path = modelData[" path" ]
486+ root . executeAfterFrameRendered (openProject, path)
426487 }
427488 }
428489
@@ -462,8 +523,17 @@ Page {
462523
463524 BusyIndicator {
464525 anchors .centerIn : parent
465- running: homepageGridView .visible && projectContent .thumbnailBusy
466- visible: homepageGridView .visible && projectContent .thumbnailBusy
526+ running: (homepageGridView .visible && projectContent .thumbnailBusy ) || (root .isLoading && index === homepageGridView .loadingIndex )
527+ visible: running
528+ }
529+
530+ // Highlight overlay shown when this project is being loaded
531+ Rectangle {
532+ anchors .fill : parent
533+ color: " transparent"
534+ border .color : palette .highlight
535+ border .width : 2
536+ visible: root .isLoading && index === homepageGridView .loadingIndex
467537 }
468538 }
469539 Label {
0 commit comments