mirror of
https://github.com/packwiz/packwiz-installer.git
synced 2025-10-26 17:44:31 +01:00
Port UI to Kotlin
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
package link.infra.packwiz.installer.ui
|
||||
|
||||
import link.infra.packwiz.installer.ui.IExceptionDetails.ExceptionListResult
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.Future
|
||||
|
||||
class CLIHandler : IUserInterface {
|
||||
override fun handleException(e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
override fun show(handler: InputStateHandler) {}
|
||||
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)
|
||||
println(sb.toString())
|
||||
}
|
||||
|
||||
override fun executeManager(task: () -> Unit) {
|
||||
task()
|
||||
println("Finished successfully!")
|
||||
}
|
||||
|
||||
override fun showOptions(options: List<IOptionDetails>): Future<Boolean> {
|
||||
for (opt in options) {
|
||||
opt.optionValue = true
|
||||
// TODO: implement option choice in the CLI?
|
||||
println("Warning: accepting option " + opt.name + " as option choosing is not implemented in the CLI")
|
||||
}
|
||||
return CompletableFuture<Boolean>().apply {
|
||||
complete(false) // Can't be cancelled!
|
||||
}
|
||||
}
|
||||
|
||||
override fun showExceptions(exceptions: List<IExceptionDetails>, numTotal: Int, allowsIgnore: Boolean): Future<ExceptionListResult> {
|
||||
val future = CompletableFuture<ExceptionListResult>()
|
||||
future.complete(ExceptionListResult.CANCEL)
|
||||
return future
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package link.infra.packwiz.installer.ui
|
||||
|
||||
import link.infra.packwiz.installer.ui.IExceptionDetails.ExceptionListResult
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Desktop
|
||||
import java.awt.event.WindowAdapter
|
||||
import java.awt.event.WindowEvent
|
||||
import java.io.IOException
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import javax.swing.*
|
||||
import javax.swing.border.EmptyBorder
|
||||
|
||||
internal class ExceptionListWindow(eList: List<IExceptionDetails>, future: CompletableFuture<ExceptionListResult>, numTotal: Int, allowsIgnore: Boolean, parentWindow: JFrame?) : JDialog(parentWindow, "Failed file downloads", true) {
|
||||
private val lblExceptionStacktrace: JTextArea
|
||||
|
||||
private class ExceptionListModel internal constructor(private val details: List<IExceptionDetails>) : AbstractListModel<String>() {
|
||||
override fun getSize() = details.size
|
||||
override fun getElementAt(index: Int) = details[index].name
|
||||
fun getExceptionAt(index: Int) = details[index].exception
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the dialog.
|
||||
*/
|
||||
init {
|
||||
setBounds(100, 100, 540, 340)
|
||||
setLocationRelativeTo(parentWindow)
|
||||
|
||||
contentPane.apply {
|
||||
layout = BorderLayout()
|
||||
|
||||
// Error panel
|
||||
add(JPanel().apply {
|
||||
add(JLabel("One or more errors were encountered while installing the modpack!").apply {
|
||||
icon = UIManager.getIcon("OptionPane.warningIcon")
|
||||
})
|
||||
}, BorderLayout.NORTH)
|
||||
|
||||
// Content panel
|
||||
add(JPanel().apply {
|
||||
border = EmptyBorder(5, 5, 5, 5)
|
||||
layout = BorderLayout(0, 0)
|
||||
|
||||
add(JSplitPane().apply {
|
||||
resizeWeight = 0.3
|
||||
|
||||
lblExceptionStacktrace = JTextArea("Select a file")
|
||||
lblExceptionStacktrace.background = UIManager.getColor("List.background")
|
||||
lblExceptionStacktrace.isOpaque = true
|
||||
lblExceptionStacktrace.wrapStyleWord = true
|
||||
lblExceptionStacktrace.lineWrap = true
|
||||
lblExceptionStacktrace.isEditable = false
|
||||
lblExceptionStacktrace.isFocusable = true
|
||||
lblExceptionStacktrace.font = UIManager.getFont("Label.font")
|
||||
lblExceptionStacktrace.border = EmptyBorder(5, 5, 5, 5)
|
||||
|
||||
rightComponent = JScrollPane(lblExceptionStacktrace)
|
||||
|
||||
leftComponent = JScrollPane(JList<String>().apply {
|
||||
selectionMode = ListSelectionModel.SINGLE_SELECTION
|
||||
border = EmptyBorder(5, 5, 5, 5)
|
||||
val listModel = ExceptionListModel(eList)
|
||||
model = listModel
|
||||
addListSelectionListener {
|
||||
val i = selectedIndex
|
||||
if (i > -1) {
|
||||
val sw = StringWriter()
|
||||
listModel.getExceptionAt(i).printStackTrace(PrintWriter(sw))
|
||||
lblExceptionStacktrace.text = sw.toString()
|
||||
// Scroll to the top
|
||||
lblExceptionStacktrace.caretPosition = 0
|
||||
} else {
|
||||
lblExceptionStacktrace.text = "Select a file"
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}, BorderLayout.CENTER)
|
||||
|
||||
// Button pane
|
||||
add(JPanel().apply {
|
||||
layout = BorderLayout(0, 0)
|
||||
|
||||
// Right buttons
|
||||
add(JPanel().apply {
|
||||
add(JButton("Continue").apply {
|
||||
toolTipText = "Attempt to continue installing, excluding the failed downloads"
|
||||
addActionListener {
|
||||
future.complete(ExceptionListResult.CONTINUE)
|
||||
this@ExceptionListWindow.dispose()
|
||||
}
|
||||
})
|
||||
|
||||
add(JButton("Cancel launch").apply {
|
||||
toolTipText = "Stop launching the game"
|
||||
addActionListener {
|
||||
future.complete(ExceptionListResult.CANCEL)
|
||||
this@ExceptionListWindow.dispose()
|
||||
}
|
||||
})
|
||||
|
||||
add(JButton("Ignore update").apply {
|
||||
toolTipText = "Start the game without attempting to update"
|
||||
isEnabled = allowsIgnore
|
||||
addActionListener {
|
||||
future.complete(ExceptionListResult.IGNORE)
|
||||
this@ExceptionListWindow.dispose()
|
||||
}
|
||||
})
|
||||
}, BorderLayout.EAST)
|
||||
|
||||
// Errored label
|
||||
add(JLabel(eList.size.toString() + "/" + numTotal + " errored").apply {
|
||||
horizontalAlignment = SwingConstants.CENTER
|
||||
}, BorderLayout.CENTER)
|
||||
|
||||
// Left buttons
|
||||
add(JPanel().apply {
|
||||
add(JButton("Report issue").apply {
|
||||
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
|
||||
addActionListener {
|
||||
try {
|
||||
Desktop.getDesktop().browse(URI("https://github.com/comp500/packwiz-installer/issues/new"))
|
||||
} catch (e: IOException) {
|
||||
// lol the button just won't work i guess
|
||||
} catch (e: URISyntaxException) {}
|
||||
}
|
||||
} else {
|
||||
isEnabled = false
|
||||
}
|
||||
})
|
||||
}, BorderLayout.WEST)
|
||||
}, BorderLayout.SOUTH)
|
||||
}
|
||||
|
||||
addWindowListener(object : WindowAdapter() {
|
||||
override fun windowClosing(e: WindowEvent) {
|
||||
future.complete(ExceptionListResult.CANCEL)
|
||||
}
|
||||
|
||||
override fun windowClosed(e: WindowEvent) {
|
||||
// Just in case closing didn't get triggered - if something else called dispose() the
|
||||
// future will have already completed
|
||||
future.complete(ExceptionListResult.CANCEL)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package link.infra.packwiz.installer.ui
|
||||
|
||||
interface IExceptionDetails {
|
||||
val exception: Exception
|
||||
val name: String
|
||||
|
||||
enum class ExceptionListResult {
|
||||
CONTINUE, CANCEL, IGNORE
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package link.infra.packwiz.installer.ui
|
||||
|
||||
interface IOptionDetails {
|
||||
val name: String
|
||||
var optionValue: Boolean
|
||||
val optionDescription: String
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package link.infra.packwiz.installer.ui
|
||||
|
||||
import link.infra.packwiz.installer.ui.IExceptionDetails.ExceptionListResult
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.Future
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
interface IUserInterface {
|
||||
fun show(handler: InputStateHandler)
|
||||
fun handleException(e: Exception)
|
||||
@JvmDefault
|
||||
fun handleExceptionAndExit(e: Exception) {
|
||||
handleException(e)
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
@JvmDefault
|
||||
fun setTitle(title: String) {}
|
||||
fun submitProgress(progress: InstallProgress)
|
||||
fun executeManager(task: () -> Unit)
|
||||
// Return true if the installation was cancelled!
|
||||
fun showOptions(options: List<IOptionDetails>): Future<Boolean>
|
||||
|
||||
fun showExceptions(exceptions: List<IExceptionDetails>, numTotal: Int, allowsIgnore: Boolean): Future<ExceptionListResult>
|
||||
@JvmDefault
|
||||
fun disableOptionsButton() {}
|
||||
// Should not return CONTINUE
|
||||
@JvmDefault
|
||||
fun showCancellationDialog(): Future<ExceptionListResult> {
|
||||
return CompletableFuture<ExceptionListResult>().apply {
|
||||
complete(ExceptionListResult.CANCEL)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package link.infra.packwiz.installer.ui
|
||||
|
||||
class InputStateHandler {
|
||||
// TODO: convert to coroutines/locks?
|
||||
@get:Synchronized
|
||||
var optionsButton = false
|
||||
private set
|
||||
@get:Synchronized
|
||||
var cancelButton = false
|
||||
private set
|
||||
|
||||
@Synchronized
|
||||
fun pressCancelButton() {
|
||||
cancelButton = true
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun pressOptionsButton() {
|
||||
optionsButton = true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package link.infra.packwiz.installer.ui
|
||||
|
||||
data class InstallProgress(
|
||||
val message: String,
|
||||
val hasProgress: Boolean = false,
|
||||
val progress: Int = 0,
|
||||
val progressTotal: Int = 0
|
||||
) {
|
||||
constructor(message: String, progress: Int, progressTotal: Int) : this(message, true, progress, progressTotal)
|
||||
|
||||
constructor(message: String) : this(message, false)
|
||||
}
|
||||
219
src/main/kotlin/link/infra/packwiz/installer/ui/InstallWindow.kt
Normal file
219
src/main/kotlin/link/infra/packwiz/installer/ui/InstallWindow.kt
Normal file
@@ -0,0 +1,219 @@
|
||||
package link.infra.packwiz.installer.ui
|
||||
|
||||
import link.infra.packwiz.installer.ui.IExceptionDetails.ExceptionListResult
|
||||
import java.awt.*
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.Future
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import javax.swing.*
|
||||
import javax.swing.border.EmptyBorder
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
class InstallWindow : IUserInterface {
|
||||
private val frmPackwizlauncher: JFrame
|
||||
private val lblProgresslabel: JLabel
|
||||
private val progressBar: JProgressBar
|
||||
private val btnOptions: JButton
|
||||
|
||||
private var inputStateHandler: InputStateHandler? = null
|
||||
private var title = "Updating modpack..."
|
||||
private var worker: SwingWorkerButWithPublicPublish<Unit, InstallProgress>? = null
|
||||
private val aboutToCrash = AtomicBoolean()
|
||||
|
||||
// TODO: separate JFrame junk from IUserInterface junk?
|
||||
|
||||
init {
|
||||
frmPackwizlauncher = JFrame().apply {
|
||||
title = this@InstallWindow.title
|
||||
setBounds(100, 100, 493, 95)
|
||||
defaultCloseOperation = JFrame.EXIT_ON_CLOSE
|
||||
setLocationRelativeTo(null)
|
||||
|
||||
// Progress bar and loading text
|
||||
add(JPanel().apply {
|
||||
border = EmptyBorder(10, 10, 10, 10)
|
||||
layout = BorderLayout(0, 0)
|
||||
|
||||
progressBar = JProgressBar().apply {
|
||||
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
|
||||
inputStateHandler?.pressOptionsButton()
|
||||
}
|
||||
}
|
||||
add(btnOptions, GridBagConstraints().apply {
|
||||
gridx = 0
|
||||
gridy = 0
|
||||
})
|
||||
|
||||
add(JButton("Cancel").apply {
|
||||
addActionListener {
|
||||
isEnabled = false
|
||||
inputStateHandler?.pressCancelButton()
|
||||
}
|
||||
}, GridBagConstraints().apply {
|
||||
gridx = 0
|
||||
gridy = 1
|
||||
})
|
||||
}, BorderLayout.EAST)
|
||||
}
|
||||
}
|
||||
|
||||
override fun show(handler: InputStateHandler) {
|
||||
inputStateHandler = handler
|
||||
EventQueue.invokeLater {
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())
|
||||
frmPackwizlauncher.isVisible = true
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleException(e: Exception) {
|
||||
e.printStackTrace()
|
||||
EventQueue.invokeLater {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"An error occurred: \n" + e.javaClass.canonicalName + ": " + e.message,
|
||||
title, JOptionPane.ERROR_MESSAGE)
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleExceptionAndExit(e: Exception) {
|
||||
e.printStackTrace()
|
||||
// TODO: Fix this mess
|
||||
// Used to prevent the done() handler of SwingWorker executing if the invokeLater hasn't happened yet
|
||||
aboutToCrash.set(true)
|
||||
EventQueue.invokeLater {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"A fatal error occurred: \n" + e.javaClass.canonicalName + ": " + e.message,
|
||||
title, JOptionPane.ERROR_MESSAGE)
|
||||
exitProcess(1)
|
||||
}
|
||||
// Pause forever, so it blocks while we wait for System.exit to take effect
|
||||
try {
|
||||
Thread.currentThread().join()
|
||||
} catch (ex: InterruptedException) { // no u
|
||||
}
|
||||
}
|
||||
|
||||
override fun setTitle(title: String) {
|
||||
this.title = title
|
||||
frmPackwizlauncher.let { frame ->
|
||||
EventQueue.invokeLater { frame.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())
|
||||
worker?.publishPublic(progress)
|
||||
}
|
||||
|
||||
override fun executeManager(task: Function0<Unit>) {
|
||||
EventQueue.invokeLater {
|
||||
// TODO: rewrite this stupidity to use channels??!!!
|
||||
worker = object : SwingWorkerButWithPublicPublish<Unit, InstallProgress>() {
|
||||
override fun doInBackground() {
|
||||
task.invoke()
|
||||
}
|
||||
|
||||
override fun process(chunks: List<InstallProgress>) {
|
||||
// Only process last chunk
|
||||
if (chunks.isNotEmpty()) {
|
||||
val (message, hasProgress, progress, progressTotal) = chunks[chunks.size - 1]
|
||||
if (hasProgress) {
|
||||
progressBar.isIndeterminate = false
|
||||
progressBar.value = progress
|
||||
progressBar.maximum = progressTotal
|
||||
} else {
|
||||
progressBar.isIndeterminate = true
|
||||
progressBar.value = 0
|
||||
}
|
||||
lblProgresslabel.text = message
|
||||
}
|
||||
}
|
||||
|
||||
override fun done() {
|
||||
if (aboutToCrash.get()) {
|
||||
return
|
||||
}
|
||||
// TODO: a better way to do this?
|
||||
frmPackwizlauncher.dispose()
|
||||
println("Finished successfully!")
|
||||
exitProcess(0)
|
||||
}
|
||||
}.also {
|
||||
it.execute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun showOptions(options: List<IOptionDetails>): Future<Boolean> {
|
||||
val future = CompletableFuture<Boolean>()
|
||||
EventQueue.invokeLater {
|
||||
OptionsSelectWindow(options, future, frmPackwizlauncher).apply {
|
||||
defaultCloseOperation = JDialog.DISPOSE_ON_CLOSE
|
||||
isVisible = true
|
||||
}
|
||||
}
|
||||
return future
|
||||
}
|
||||
|
||||
override fun showExceptions(exceptions: List<IExceptionDetails>, 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() {
|
||||
btnOptions.apply {
|
||||
text = "Optional mods..."
|
||||
isEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun showCancellationDialog(): Future<ExceptionListResult> {
|
||||
val future = CompletableFuture<ExceptionListResult>()
|
||||
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) ExceptionListResult.CANCEL else ExceptionListResult.IGNORE)
|
||||
}
|
||||
return future
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package link.infra.packwiz.installer.ui
|
||||
|
||||
// Serves as a proxy for IOptionDetails, so that setOptionValue isn't called until OK is clicked
|
||||
internal class OptionTempHandler(private val opt: IOptionDetails) : IOptionDetails {
|
||||
override var optionValue = opt.optionValue
|
||||
|
||||
override val name get() = opt.name
|
||||
override val optionDescription get() = opt.optionDescription
|
||||
|
||||
fun finalise() {
|
||||
opt.optionValue = optionValue
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package link.infra.packwiz.installer.ui
|
||||
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.FlowLayout
|
||||
import java.awt.event.ActionEvent
|
||||
import java.awt.event.ActionListener
|
||||
import java.awt.event.WindowAdapter
|
||||
import java.awt.event.WindowEvent
|
||||
import java.util.*
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import javax.swing.*
|
||||
import javax.swing.border.EmptyBorder
|
||||
import javax.swing.event.TableModelListener
|
||||
import javax.swing.table.TableModel
|
||||
|
||||
class OptionsSelectWindow internal constructor(optList: List<IOptionDetails>, future: CompletableFuture<Boolean>, parentWindow: JFrame?) : JDialog(parentWindow, "Select optional mods...", true), ActionListener {
|
||||
private val lblOptionDescription: JTextArea
|
||||
private val tableModel: OptionTableModel
|
||||
private val future: CompletableFuture<Boolean>
|
||||
|
||||
private class OptionTableModel internal constructor(givenOpts: List<IOptionDetails>) : TableModel {
|
||||
private val opts: List<OptionTempHandler>
|
||||
|
||||
init {
|
||||
val mutOpts = ArrayList<OptionTempHandler>()
|
||||
for (opt in givenOpts) {
|
||||
mutOpts.add(OptionTempHandler(opt))
|
||||
}
|
||||
opts = mutOpts
|
||||
}
|
||||
|
||||
override fun getRowCount() = opts.size
|
||||
override fun getColumnCount() = 2
|
||||
|
||||
private val columnNames = arrayOf("Enabled", "Mod name")
|
||||
private val columnTypes = arrayOf(Boolean::class.javaObjectType, String::class.java)
|
||||
private val columnEditables = booleanArrayOf(true, false)
|
||||
|
||||
override fun getColumnName(columnIndex: Int) = columnNames[columnIndex]
|
||||
override fun getColumnClass(columnIndex: Int) = columnTypes[columnIndex]
|
||||
override fun isCellEditable(rowIndex: Int, columnIndex: Int) = columnEditables[columnIndex]
|
||||
|
||||
override fun getValueAt(rowIndex: Int, columnIndex: Int): Any {
|
||||
val opt = opts[rowIndex]
|
||||
return if (columnIndex == 0) opt.optionValue else opt.name
|
||||
}
|
||||
|
||||
override fun setValueAt(aValue: Any, rowIndex: Int, columnIndex: Int) {
|
||||
if (columnIndex == 0) {
|
||||
val opt = opts[rowIndex]
|
||||
opt.optionValue = aValue as Boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Noop, the table model doesn't change!
|
||||
override fun addTableModelListener(l: TableModelListener) {}
|
||||
override fun removeTableModelListener(l: TableModelListener) {}
|
||||
|
||||
fun getDescription(rowIndex: Int) = opts[rowIndex].optionDescription
|
||||
|
||||
fun finalise() {
|
||||
for (opt in opts) {
|
||||
opt.finalise()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun actionPerformed(e: ActionEvent) {
|
||||
if (e.actionCommand == "OK") {
|
||||
tableModel.finalise()
|
||||
future.complete(false)
|
||||
dispose()
|
||||
} else if (e.actionCommand == "Cancel") {
|
||||
future.complete(true)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the dialog.
|
||||
*/
|
||||
init {
|
||||
tableModel = OptionTableModel(optList)
|
||||
this.future = future
|
||||
|
||||
setBounds(100, 100, 450, 300)
|
||||
setLocationRelativeTo(parentWindow)
|
||||
|
||||
contentPane.apply {
|
||||
layout = BorderLayout()
|
||||
add(JPanel().apply {
|
||||
border = EmptyBorder(5, 5, 5, 5)
|
||||
layout = BorderLayout(0, 0)
|
||||
|
||||
add(JSplitPane().apply {
|
||||
resizeWeight = 0.5
|
||||
|
||||
lblOptionDescription = JTextArea("Select an option...").apply {
|
||||
background = UIManager.getColor("List.background")
|
||||
isOpaque = true
|
||||
wrapStyleWord = true
|
||||
lineWrap = true
|
||||
isEditable = false
|
||||
isFocusable = false
|
||||
font = UIManager.getFont("Label.font")
|
||||
border = EmptyBorder(10, 10, 10, 10)
|
||||
}
|
||||
|
||||
leftComponent = JScrollPane(JTable().apply {
|
||||
showVerticalLines = false
|
||||
showHorizontalLines = false
|
||||
setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||
setShowGrid(false)
|
||||
model = tableModel
|
||||
columnModel.getColumn(0).resizable = false
|
||||
columnModel.getColumn(0).preferredWidth = 15
|
||||
columnModel.getColumn(0).maxWidth = 15
|
||||
columnModel.getColumn(1).resizable = false
|
||||
selectionModel.addListSelectionListener {
|
||||
val i = selectedRow
|
||||
if (i > -1) {
|
||||
lblOptionDescription.text = tableModel.getDescription(i)
|
||||
} else {
|
||||
lblOptionDescription.text = "Select an option..."
|
||||
}
|
||||
}
|
||||
tableHeader = null
|
||||
}).apply {
|
||||
viewport.background = UIManager.getColor("List.background")
|
||||
horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
|
||||
}
|
||||
|
||||
rightComponent = JScrollPane(lblOptionDescription)
|
||||
})
|
||||
|
||||
add(JPanel().apply {
|
||||
layout = FlowLayout(FlowLayout.RIGHT)
|
||||
|
||||
add(JButton("OK").apply {
|
||||
actionCommand = "OK"
|
||||
addActionListener(this@OptionsSelectWindow)
|
||||
|
||||
this@OptionsSelectWindow.rootPane.defaultButton = this
|
||||
})
|
||||
|
||||
add(JButton("Cancel").apply {
|
||||
actionCommand = "Cancel"
|
||||
addActionListener(this@OptionsSelectWindow)
|
||||
})
|
||||
}, BorderLayout.SOUTH)
|
||||
}, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
addWindowListener(object : WindowAdapter() {
|
||||
override fun windowClosing(e: WindowEvent) {
|
||||
future.complete(true)
|
||||
}
|
||||
|
||||
override fun windowClosed(e: WindowEvent) {
|
||||
// Just in case closing didn't get triggered - if something else called dispose() the
|
||||
// future will have already completed
|
||||
future.complete(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package link.infra.packwiz.installer.ui
|
||||
|
||||
import javax.swing.SwingWorker
|
||||
|
||||
// Q: AAA WHAT HAVE YOU DONE THIS IS DISGUSTING
|
||||
// A: it just makes things easier, so i can easily have one interface for CLI/GUI
|
||||
// if someone has a better way to do this please PR it
|
||||
abstract class SwingWorkerButWithPublicPublish<T, V> : SwingWorker<T, V>() {
|
||||
@SafeVarargs
|
||||
fun publishPublic(vararg chunks: V) {
|
||||
publish(*chunks)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user