Separate IUserInterface logic out into GUIHandler

This commit is contained in:
comp500 2020-12-13 16:34:00 +00:00
parent f5b22f37a4
commit 0df48d19a9
3 changed files with 195 additions and 179 deletions

View File

@ -4,7 +4,7 @@ package link.infra.packwiz.installer
import link.infra.packwiz.installer.metadata.SpaceSafeURI import link.infra.packwiz.installer.metadata.SpaceSafeURI
import link.infra.packwiz.installer.ui.cli.CLIHandler import link.infra.packwiz.installer.ui.cli.CLIHandler
import link.infra.packwiz.installer.ui.gui.InstallWindow import link.infra.packwiz.installer.ui.gui.GUIHandler
import org.apache.commons.cli.DefaultParser import org.apache.commons.cli.DefaultParser
import org.apache.commons.cli.Options import org.apache.commons.cli.Options
import org.apache.commons.cli.ParseException import org.apache.commons.cli.ParseException
@ -47,7 +47,7 @@ class Main(args: Array<String>) {
guiEnabled = false guiEnabled = false
} }
val ui = if (guiEnabled) InstallWindow() else CLIHandler() val ui = if (guiEnabled) GUIHandler() else CLIHandler()
val unparsedArgs = cmd.args val unparsedArgs = cmd.args
if (unparsedArgs.size > 1) { if (unparsedArgs.size > 1) {

View File

@ -0,0 +1,134 @@
package link.infra.packwiz.installer.ui.gui
import link.infra.packwiz.installer.ui.IUserInterface
import link.infra.packwiz.installer.ui.IUserInterface.ExceptionListResult
import link.infra.packwiz.installer.ui.data.ExceptionDetails
import link.infra.packwiz.installer.ui.data.IOptionDetails
import link.infra.packwiz.installer.ui.data.InstallProgress
import java.awt.EventQueue
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Future
import javax.swing.JDialog
import javax.swing.JOptionPane
import javax.swing.UIManager
import kotlin.system.exitProcess
class GUIHandler : IUserInterface {
private lateinit var frmPackwizlauncher: InstallWindow
@Volatile
override var optionsButtonPressed = false
@Volatile
override var cancelButtonPressed = false
private var title = "Updating modpack..."
init {
EventQueue.invokeAndWait {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())
} catch (e: Exception) {
println("Failed to set look and feel:")
e.printStackTrace()
}
frmPackwizlauncher = InstallWindow(this).apply {
title = this@GUIHandler.title
}
}
}
override fun show() = EventQueue.invokeLater {
frmPackwizlauncher.isVisible = true
}
override fun dispose() = EventQueue.invokeAndWait {
frmPackwizlauncher.dispose()
}
override fun handleException(e: Exception) {
e.printStackTrace()
EventQueue.invokeAndWait {
JOptionPane.showMessageDialog(null,
"An error occurred: \n" + e.javaClass.canonicalName + ": " + e.message,
title, JOptionPane.ERROR_MESSAGE)
}
}
override fun handleExceptionAndExit(e: Exception) {
e.printStackTrace()
EventQueue.invokeAndWait {
JOptionPane.showMessageDialog(null,
"A fatal error occurred: \n" + e.javaClass.canonicalName + ": " + e.message,
title, JOptionPane.ERROR_MESSAGE)
exitProcess(1)
}
}
override fun setTitle(title: String) {
this.title = title
EventQueue.invokeLater { frmPackwizlauncher.title = title }
}
override fun submitProgress(progress: InstallProgress) {
val sb = StringBuilder()
if (progress.hasProgress) {
sb.append('(')
sb.append(progress.progress)
sb.append('/')
sb.append(progress.progressTotal)
sb.append(") ")
}
sb.append(progress.message)
// TODO: better logging library?
println(sb.toString())
EventQueue.invokeLater {
frmPackwizlauncher.displayProgress(progress)
}
}
override fun showOptions(options: List<IOptionDetails>): Future<Boolean> {
val future = CompletableFuture<Boolean>()
EventQueue.invokeLater {
if (options.isEmpty()) {
JOptionPane.showMessageDialog(null,
"This modpack has no optional mods!",
"Optional mods", JOptionPane.INFORMATION_MESSAGE)
future.complete(false)
} else {
OptionsSelectWindow(options, future, frmPackwizlauncher).apply {
defaultCloseOperation = JDialog.DISPOSE_ON_CLOSE
isVisible = true
}
}
}
return future
}
override fun showExceptions(exceptions: List<ExceptionDetails>, numTotal: Int, allowsIgnore: Boolean): Future<ExceptionListResult> {
val future = CompletableFuture<ExceptionListResult>()
EventQueue.invokeLater {
ExceptionListWindow(exceptions, future, numTotal, allowsIgnore, frmPackwizlauncher).apply {
defaultCloseOperation = JDialog.DISPOSE_ON_CLOSE
isVisible = true
}
}
return future
}
override fun disableOptionsButton() = EventQueue.invokeLater {
frmPackwizlauncher.disableOptionsButton()
}
override fun showCancellationDialog(): Future<IUserInterface.CancellationResult> {
val future = CompletableFuture<IUserInterface.CancellationResult>()
EventQueue.invokeLater {
val buttons = arrayOf("Quit", "Ignore")
val result = JOptionPane.showOptionDialog(frmPackwizlauncher,
"The installation was cancelled. Would you like to quit the game, or ignore the update and start the game?",
"Cancelled installation",
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, buttons, buttons[0])
future.complete(if (result == 0) IUserInterface.CancellationResult.QUIT else IUserInterface.CancellationResult.CONTINUE)
}
return future
}
}

View File

@ -1,202 +1,84 @@
package link.infra.packwiz.installer.ui.gui package link.infra.packwiz.installer.ui.gui
import link.infra.packwiz.installer.ui.IUserInterface
import link.infra.packwiz.installer.ui.IUserInterface.ExceptionListResult
import link.infra.packwiz.installer.ui.data.ExceptionDetails
import link.infra.packwiz.installer.ui.data.IOptionDetails
import link.infra.packwiz.installer.ui.data.InstallProgress import link.infra.packwiz.installer.ui.data.InstallProgress
import java.awt.* import java.awt.BorderLayout
import java.util.concurrent.CompletableFuture import java.awt.Component
import java.util.concurrent.Future import java.awt.GridBagConstraints
import java.awt.GridBagLayout
import javax.swing.* import javax.swing.*
import javax.swing.border.EmptyBorder import javax.swing.border.EmptyBorder
import kotlin.system.exitProcess
class InstallWindow : IUserInterface { class InstallWindow(private val handler: GUIHandler) : JFrame() {
private lateinit var frmPackwizlauncher: JFrame private var lblProgresslabel: JLabel
private lateinit var lblProgresslabel: JLabel private var progressBar: JProgressBar
private lateinit var progressBar: JProgressBar private var btnOptions: JButton
private lateinit var btnOptions: JButton
@Volatile
override var optionsButtonPressed = false
@Volatile
override var cancelButtonPressed = false
private var title = "Updating modpack..."
// TODO: separate JFrame junk from IUserInterface junk?
init { init {
EventQueue.invokeAndWait { setBounds(100, 100, 493, 95)
frmPackwizlauncher = JFrame().apply { defaultCloseOperation = EXIT_ON_CLOSE
title = this@InstallWindow.title setLocationRelativeTo(null)
setBounds(100, 100, 493, 95)
defaultCloseOperation = JFrame.EXIT_ON_CLOSE
setLocationRelativeTo(null)
// Progress bar and loading text // Progress bar and loading text
add(JPanel().apply { add(JPanel().apply {
border = EmptyBorder(10, 10, 10, 10) border = EmptyBorder(10, 10, 10, 10)
layout = BorderLayout(0, 0) layout = BorderLayout(0, 0)
progressBar = JProgressBar().apply { progressBar = JProgressBar().apply {
isIndeterminate = true isIndeterminate = true
}
add(progressBar, BorderLayout.CENTER)
lblProgresslabel = JLabel("Loading...")
add(lblProgresslabel, BorderLayout.SOUTH)
}, BorderLayout.CENTER)
// Buttons
add(JPanel().apply {
border = EmptyBorder(0, 5, 0, 5)
layout = GridBagLayout()
btnOptions = JButton("Optional mods...").apply {
alignmentX = Component.CENTER_ALIGNMENT
addActionListener {
text = "Loading..."
isEnabled = false
optionsButtonPressed = true
}
}
add(btnOptions, GridBagConstraints().apply {
gridx = 0
gridy = 0
})
add(JButton("Cancel").apply {
addActionListener {
isEnabled = false
cancelButtonPressed = true
}
}, GridBagConstraints().apply {
gridx = 0
gridy = 1
})
}, BorderLayout.EAST)
} }
} add(progressBar, BorderLayout.CENTER)
}
override fun show() { lblProgresslabel = JLabel("Loading...")
EventQueue.invokeLater { add(lblProgresslabel, BorderLayout.SOUTH)
try { }, BorderLayout.CENTER)
// TODO: shouldn't we do this before everything else?
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())
frmPackwizlauncher.isVisible = true
} catch (e: Exception) {
e.printStackTrace()
}
}
}
override fun dispose() { // Buttons
EventQueue.invokeAndWait { add(JPanel().apply {
frmPackwizlauncher.dispose() border = EmptyBorder(0, 5, 0, 5)
} layout = GridBagLayout()
}
override fun handleException(e: Exception) { btnOptions = JButton("Optional mods...").apply {
e.printStackTrace() alignmentX = Component.CENTER_ALIGNMENT
EventQueue.invokeAndWait {
JOptionPane.showMessageDialog(null,
"An error occurred: \n" + e.javaClass.canonicalName + ": " + e.message,
title, JOptionPane.ERROR_MESSAGE)
}
}
override fun handleExceptionAndExit(e: Exception) { addActionListener {
e.printStackTrace() text = "Loading..."
EventQueue.invokeAndWait { isEnabled = false
JOptionPane.showMessageDialog(null, handler.optionsButtonPressed = true
"A fatal error occurred: \n" + e.javaClass.canonicalName + ": " + e.message,
title, JOptionPane.ERROR_MESSAGE)
exitProcess(1)
}
}
override fun setTitle(title: String) {
this.title = title
EventQueue.invokeLater { frmPackwizlauncher.title = title }
}
override fun submitProgress(progress: InstallProgress) {
val sb = StringBuilder()
if (progress.hasProgress) {
sb.append('(')
sb.append(progress.progress)
sb.append('/')
sb.append(progress.progressTotal)
sb.append(") ")
}
sb.append(progress.message)
// TODO: better logging library?
println(sb.toString())
EventQueue.invokeLater {
if (progress.hasProgress) {
progressBar.isIndeterminate = false
progressBar.value = progress.progress
progressBar.maximum = progress.progressTotal
} else {
progressBar.isIndeterminate = true
progressBar.value = 0
}
lblProgresslabel.text = progress.message
}
}
override fun showOptions(options: List<IOptionDetails>): Future<Boolean> {
val future = CompletableFuture<Boolean>()
EventQueue.invokeLater {
if (options.isEmpty()) {
JOptionPane.showMessageDialog(null,
"This modpack has no optional mods!",
"Optional mods", JOptionPane.INFORMATION_MESSAGE)
future.complete(false)
} else {
OptionsSelectWindow(options, future, frmPackwizlauncher).apply {
defaultCloseOperation = JDialog.DISPOSE_ON_CLOSE
isVisible = true
} }
} }
} add(btnOptions, GridBagConstraints().apply {
return future gridx = 0
gridy = 0
})
add(JButton("Cancel").apply {
addActionListener {
isEnabled = false
handler.cancelButtonPressed = true
}
}, GridBagConstraints().apply {
gridx = 0
gridy = 1
})
}, BorderLayout.EAST)
} }
override fun showExceptions(exceptions: List<ExceptionDetails>, numTotal: Int, allowsIgnore: Boolean): Future<ExceptionListResult> { fun displayProgress(progress: InstallProgress) {
val future = CompletableFuture<ExceptionListResult>() if (progress.hasProgress) {
EventQueue.invokeLater { progressBar.isIndeterminate = false
ExceptionListWindow(exceptions, future, numTotal, allowsIgnore, frmPackwizlauncher).apply { progressBar.value = progress.progress
defaultCloseOperation = JDialog.DISPOSE_ON_CLOSE progressBar.maximum = progress.progressTotal
isVisible = true } else {
} progressBar.isIndeterminate = true
progressBar.value = 0
} }
return future lblProgresslabel.text = progress.message
} }
override fun disableOptionsButton() { fun disableOptionsButton() {
EventQueue.invokeLater { btnOptions.apply {
btnOptions.apply { text = "No optional mods"
text = "No optional mods" isEnabled = false
isEnabled = false
}
} }
} }
override fun showCancellationDialog(): Future<IUserInterface.CancellationResult> {
val future = CompletableFuture<IUserInterface.CancellationResult>()
EventQueue.invokeLater {
val buttons = arrayOf("Quit", "Ignore")
val result = JOptionPane.showOptionDialog(frmPackwizlauncher,
"The installation was cancelled. Would you like to quit the game, or ignore the update and start the game?",
"Cancelled installation",
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, buttons, buttons[0])
future.complete(if (result == 0) IUserInterface.CancellationResult.QUIT else IUserInterface.CancellationResult.CONTINUE)
}
return future
}
} }