4747import javax .swing .JTabbedPane ;
4848import javax .swing .JTable ;
4949import javax .swing .KeyStroke ;
50+ import javax .swing .SwingUtilities ;
51+ import javax .swing .SwingWorker ;
5052import javax .swing .Timer ;
5153import javax .swing .UIManager ;
5254import javax .swing .UnsupportedLookAndFeelException ;
@@ -436,7 +438,7 @@ public void mouseReleased(MouseEvent e) { // mouseReleased event needed for othe
436438 // DETERMINE IF BACKUP RESTORE BUTTON SHOULD BE DISABLED ---
437439 // DETERMINE IF BACKUP DELETE BUTTON SHOULD BE DISABLED ---
438440 // (ie: if last backup was just deleted)
439- if (SaveManager .listAllBackups ().size () == 0 || this .saveBackupsTable .getSelectedRow () == -1 ) {
441+ if (SaveManager .listAllBackups ().isEmpty () || this .saveBackupsTable .getSelectedRow () == -1 ) {
440442 this .backupDeleteButton .setEnabled (false );
441443 this .backupRestoreButton .setEnabled (false );
442444 }
@@ -460,23 +462,40 @@ private Runnable setupSoundpacksGui() {
460462 int result = fileChooser .showSaveDialog (mainPanel );
461463
462464 if (result == JFileChooser .APPROVE_OPTION && fileChooser .getSelectedFile () != null ) {
465+
466+ // enable global progressbar
467+ this .globalProgressBar .setEnabled (true );
468+
463469 // setup dummy timer to give user visual feedback that his operation is in progress...
464470 Timer dummyTimer = new Timer (10 , e1 -> {
465- if (this .globalProgressBar .getValue () <= 100 ) {
471+ if (this .globalProgressBar .getValue () < 99 ) { // it's important to keep this from hitting 100% while it is in its dummy-loop...
466472 this .globalProgressBar .setValue (this .globalProgressBar .getValue () + 1 );
467473 }
468474 });
469475
470476 // start timer before triggering installation
471477 dummyTimer .start ();
472478
473- // start installation and give it a callback to stop the dummy timer
474- SoundpackManager .installSoundpack (fileChooser .getSelectedFile (), p -> dummyTimer .stop ());
479+ // execute the installation in a background thread, outside the Event Dispatch Thread (EDT)
480+ SwingWorker <Void , Void > worker = new SwingWorker <>() {
481+ @ Override
482+ protected Void doInBackground () {
483+ SoundpackManager .installSoundpack (fileChooser .getSelectedFile (), p -> dummyTimer .stop ());
484+ return null ;
485+ }
486+
487+ @ Override
488+ protected void done () {
489+ dummyTimer .stop (); // ensure the timer is stopped when the task is complete
490+ globalProgressBar .setValue (100 ); // this will refresh the GUI upon hitting 100%
491+ }
492+ };
493+
494+ // start the worker thread
495+ worker .execute ();
475496 } else {
476497 LOGGER .trace ("Exiting soundpack finder dialog with no selection..." );
477498 }
478-
479- this .refreshGuiElements ();
480499 });
481500
482501 // SOUNDPACK DELETE BUTTON LISTENER ---
@@ -564,7 +583,7 @@ public void mouseReleased(MouseEvent e) { // mouseReleased event needed for othe
564583
565584 // DETERMINE IF SOUNDPACK DELETE BUTTON SHOULD BE DISABLED ---
566585 // (ie: if last backup was just deleted)
567- if (SoundpackManager .listAllSoundpacks ().size () == 0 || this .soundpacksTable .getSelectedRow () == -1 ) {
586+ if (SoundpackManager .listAllSoundpacks ().isEmpty () || this .soundpacksTable .getSelectedRow () == -1 ) {
568587 this .uninstallSoundpackButton .setEnabled (false );
569588 }
570589 };
@@ -606,38 +625,58 @@ public String getDescription() {
606625 int result = fileChooser .showSaveDialog (mainPanel );
607626
608627 if (result == JFileChooser .APPROVE_OPTION && fileChooser .getSelectedFile () != null ) {
628+
629+ // enable global progressbar
630+ this .globalProgressBar .setEnabled (true );
631+
609632 // setup dummy timer to give user visual feedback that his operation is in progress...
610633 Timer dummyTimer = new Timer (10 , e1 -> {
611- if (this .globalProgressBar .getValue () <= 100 ) {
634+ if (this .globalProgressBar .getValue () < 99 ) { // it's important to keep this from hitting 100% while it is in its dummy-loop...
612635 this .globalProgressBar .setValue (this .globalProgressBar .getValue () + 1 );
613636 }
614637 });
615638
616639 // start timer before triggering installation
617640 dummyTimer .start ();
618641
619- // start installation and give it a callback to stop the dummy timer
620- ModManager .getInstance ().installMod (fileChooser .getSelectedFile (), p -> dummyTimer .stop ()).toEither ().fold (
621- failure -> {
622- dummyTimer .stop (); // abort dummyTimer on error
623- LOGGER .error ("There was a problem while installing mod [{}]" , fileChooser .getSelectedFile (), failure .getError ());
624-
625- ErrorDialog .showErrorDialog (
626- String .format ("There was a problem while installing mod [%s]" , fileChooser .getSelectedFile ().getName ()),
627- failure .getError ()
628- ).packCenterAndShow (this .mainPanel );
629- return null ;
630- },
631- success -> {
632- LOGGER .info ("Mod [{}] has been successfully installed!" , fileChooser .getSelectedFile ());
642+ // execute the installation in a background thread, outside the Event Dispatch Thread (EDT)
643+ SwingWorker <Void , Void > worker = new SwingWorker <>() {
644+ @ Override
645+ protected Void doInBackground () {
646+ ModManager .getInstance ().installMod (fileChooser .getSelectedFile (), p -> dummyTimer .stop ()).toEither ().fold (
647+ failure -> {
648+ LOGGER .error ("There was a problem while installing mod [{}]" , fileChooser .getSelectedFile (), failure .getError ());
649+
650+ // show error dialog on the EDT
651+ SwingUtilities .invokeLater (() -> {
652+ dummyTimer .stop (); // stop dummyTimer on error
653+ ErrorDialog .showErrorDialog (
654+ String .format ("There was a problem while installing mod [%s]" , fileChooser .getSelectedFile ().getName ()),
655+ failure .getError ()
656+ ).packCenterAndShow (mainPanel );
657+ });
658+ return null ;
659+ },
660+ success -> {
661+ LOGGER .info ("Mod [{}] has been successfully installed!" , fileChooser .getSelectedFile ());
662+ return null ;
663+ }
664+ );
633665 return null ;
634666 }
635- );
667+
668+ @ Override
669+ protected void done () {
670+ dummyTimer .stop (); // ensure the timer is stopped when the task is complete
671+ globalProgressBar .setValue (100 ); // this will refresh the GUI upon hitting 100%
672+ }
673+ };
674+
675+ // start the worker thread
676+ worker .execute ();
636677 } else {
637678 LOGGER .trace ("Exiting mod finder dialog with no selection..." );
638679 }
639-
640- this .refreshGuiElements ();
641680 });
642681
643682 // MOD DELETE BUTTON LISTENER ---
@@ -829,7 +868,7 @@ private static JMenuBar buildMenuBarFor(Component parent) {
829868 */
830869 private void refreshGuiElements () {
831870 for (Runnable guiRefreshRunnable : this .guiRefreshingRunnables ) {
832- guiRefreshRunnable . run ();
871+ new Thread ( guiRefreshRunnable ). start ();
833872 }
834873 }
835874
0 commit comments