You’re looking at the problem linearly, and I don’t blame you. It’s likely how you were taught, it’s how we were all taught, but this requires a new approach, so some clarification is in order.
Xojo’s Main Thread is an invisible event loop that does all the heavy lifting in your app. When you choose something from a menu or implement the Action event of a Pushbutton, that code all runs in the Main Thread.
Any other Threads you create will run concurrently with the Main Thread. Once started, they go off and complete their task, then end. If you put an endless loop into a Thread, it will never end, but will never tie up the Main Thread either. Once started (“Run”), a Thread returns flow to the calling Thread almost immediately.
On the other hand, any loop you put into the Main Thread will tie up the Main Thread, and thus most of your app, until the loop ends. If you create a loop that starts a Thread and then waits for it to finish before moving on, you’ve defeated the purpose of the Thread and your UI will still be locked up.
This is linear (or as you put it, sequential) programming, but the way to attack this problem is through event-driven code. You don’t keep checking if the Thread is finished to move on, you wait until the Thread tell you it’s finished, then do the next thing.
In this case, you want part of the UI to be responsive (progress bars), but not other parts (buttons, menus, etc.), but that’s not how it works, it’s either all or nothing. But you can still get the results you seek with a little extra effort.
Here’s how I’d handle it:
- Create a Task subclass designed to do this export. It will contain all the information is needs to complete its task. That can include an array of files to export.
- When the user clicks export, store this information in the Task’s properties, disable the interface, then run the Task.
- React to the Task’s events to update your progress bar. When it tells you it’s done, re-enable the interface.
At no time do you create a loop to monitor the Task, you let it feed you the information instead.
If you don’t want to handle disabling the interface, move this function to a dialog window and have it do the processing in the way I described. When the Task is finished, it will close the dialog.