Inceptum/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/window/dialog/ProcessStateWatcherDialog.kt

103 lines
3.2 KiB
Kotlin

package io.gitlab.jfronny.inceptum.gtk.window.dialog
import io.gitlab.jfronny.commons.StringFormatter
import io.gitlab.jfronny.commons.throwable.ThrowingRunnable
import io.gitlab.jfronny.inceptum.gtk.GtkEnvBackend
import io.gitlab.jfronny.inceptum.gtk.schedule
import io.gitlab.jfronny.inceptum.gtk.util.I18n
import io.gitlab.jfronny.inceptum.gtk.util.Log
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState
import org.gnome.glib.GLib
import org.gnome.gtk.*
class ProcessStateWatcherDialog(
parent: Window?,
title: String,
errorMessage: String,
private val state: ProcessState,
executor: ThrowingRunnable<*>
) : MessageDialog(
parent,
DialogFlags.MODAL.or(DialogFlags.DESTROY_WITH_PARENT),
MessageType.INFO,
ButtonsType.NONE,
null
) {
private var finished = false
private var cachedState: State? = null
init {
//TODO alternate UI: Only show progress bar by default, but have a dropdown to a "console" with the actual steps
// this should make visualizing parallelized steps easier
this.title = title
addButton(I18n["cancel"], ResponseType.CANCEL.value)
onResponse { responseId: Int ->
when (ResponseType.of(responseId)) {
ResponseType.CLOSE, ResponseType.CANCEL -> {
state.cancel()
close()
}
ResponseType.DELETE_EVENT -> destroy()
else -> Log.error("Unexpected response type: $responseId")
}
}
onCloseRequest {
if (finished) return@onCloseRequest false
state.cancel()
false
}
val progress = ProgressBar()
(messageArea as Box).append(progress)
addTickCallback { widget, _ ->
if (finished) return@addTickCallback GLib.SOURCE_REMOVE
val nc = State(state)
if (nc != cachedState) {
cachedState = nc
setMarkup(cachedState!!.msg)
progress.fraction = cachedState!!.progress.coerceAtMost(1f).toDouble()
widget.queueDraw()
}
GLib.SOURCE_CONTINUE
}
Thread {
try {
executor.run()
} catch (e: Throwable) {
state.cancel()
Log.error(errorMessage, e)
GtkEnvBackend.simpleDialog(
parent,
StringFormatter.toString(e),
errorMessage,
null,
null
)
} finally {
finished = true
schedule { close() }
}
}.start()
}
@JvmRecord
data class State(val msg: String, val progress: Float) {
constructor(source: ProcessState) : this(source.currentStep, source.progress)
}
companion object {
@JvmStatic
fun show(
parent: Window?,
title: String,
errorMessage: String,
state: ProcessState,
executor: ThrowingRunnable<*>
): ProcessStateWatcherDialog {
val dialog = ProcessStateWatcherDialog(parent, title, errorMessage, state, executor)
dialog.show()
return dialog
}
}
}