Inceptum/launcher-gtk/src/main/kotlin/io/gitlab/jfronny/inceptum/gtk/control/InstanceListEntryFactory.kt

152 lines
6.5 KiB
Kotlin

package io.gitlab.jfronny.inceptum.gtk.control
import io.gitlab.jfronny.commons.io.JFiles
import io.gitlab.jfronny.commons.ref.R
import io.gitlab.jfronny.inceptum.common.MetaHolder
import io.gitlab.jfronny.inceptum.common.Utils
import io.gitlab.jfronny.inceptum.gtk.GtkMenubar
import io.gitlab.jfronny.inceptum.gtk.menu.MenuBuilder
import io.gitlab.jfronny.inceptum.gtk.util.I18n
import io.gitlab.jfronny.inceptum.gtk.util.fixSubtitle
import io.gitlab.jfronny.inceptum.gtk.util.get
import io.gitlab.jfronny.inceptum.gtk.util.margin
import io.gitlab.jfronny.inceptum.gtk.window.settings.instance.InstanceSettingsWindow
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance
import io.gitlab.jfronny.inceptum.launcher.system.instance.InstanceNameTool
import io.gitlab.jfronny.inceptum.launcher.system.launch.LaunchType
import org.gnome.adw.ActionRow
import org.gnome.gdk.Gdk
import org.gnome.gio.Menu
import org.gnome.gtk.*
import java.io.IOException
class InstanceListEntryFactory(
private val app: Application?,
private val instanceList: List<Instance>
) : KSignalListItemFactory<InstanceListEntryFactory.Decomposed, ActionRow>() {
override fun setup(): ActionRow {
val thumbnail = InstanceThumbnail()
thumbnail.name = "inceptum-thumbnail"
val launch = Button.newFromIconName("media-playback-start-symbolic")
launch.addCssClass("flat")
launch.name = "inceptum-launch"
launch.tooltipText = I18n["instance.launch"]
val menu = MenuButton()
menu.addCssClass("flat")
menu.iconName = "view-more-symbolic"
menu.setPopover(PopoverMenu.newFromModel(Menu()))
val row = ActionRow()
row.margin = 8
row.name = "inceptum-row"
row.removeCssClass("activatable") //TODO remove this workaround if a better way to support opening the menu is found
row.addPrefix(thumbnail)
row.addSuffix(launch)
row.addSuffix(menu)
row.fixSubtitle()
val rightClicked = GestureClick()
rightClicked.button = Gdk.BUTTON_SECONDARY
rightClicked.onPressed { nPress, _, _ -> if (nPress == 1) menu.emitActivate() }
row.addController(rightClicked)
return row
}
override fun BindContext.bind(widget: ActionRow, data: Decomposed) {
if (data.instance?.isLocked ?: true) {
data.item.activatable = false
widget.subtitle = if (data.instance?.isRunningLocked ?: false) I18n["instance.launch.locked.running"]
else I18n["instance.launch.locked.setup"]
}
widget.title = data.instance.toString()
data.thumbnail.bind(data.instance!!)
val menuBuilder = MenuBuilder(data.popoverMenu, data.instanceId)
val launchSection = menuBuilder.literalSection("launch", null)
val kill = launchSection.literalButton("kill", I18n["instance.kill"]) {
//TODO test
LauncherEnv.showOkCancel(I18n["instance.kill.prompt"], I18n["instance.kill.details"]) {
if (!data.instance.kill()) LauncherEnv.showError(I18n["instance.kill.fail"], I18n["failed"])
}
}
kill.enabled = data.instance.isRunningLocked
launchSection.literalButton(
"launch.client", I18n["instance.launch.client"]
) { GtkMenubar.launch(data.instance, LaunchType.Client) }.iconName = "media-playback-start-symbolic"
launchSection.literalButton(
"launch.server", I18n["instance.launch.server"]
) { GtkMenubar.launch(data.instance, LaunchType.Server) }.iconName = "network-server-symbolic"
val settingsSection = menuBuilder.literalSection("settings", null)
settingsSection.literalButton("settings", I18n["instance.settings"]) {
//TODO keep track of properties windows and don't allow opening two
InstanceSettingsWindow(app, data.instance).visible = true
}.iconName = "document-edit-symbolic"
settingsSection.literalButton(
"directory", I18n["instance.directory"]
) { Utils.openFile(data.instance.path.toFile()) }.iconName = "folder-symbolic"
settingsSection.literalButton("copy", I18n["instance.copy"]) {
LauncherEnv.getInput(
I18n["instance.copy.prompt"],
I18n["instance.copy.details"],
InstanceNameTool.getNextValid(data.instance.name),
{ s: String? ->
try {
JFiles.copyRecursive(
data.instance.path,
MetaHolder.INSTANCE_DIR.resolve(InstanceNameTool.getNextValid(s))
)
} catch (e: IOException) {
LauncherEnv.showError(I18n["instance.copy.fail"], e)
}
}) { R.nop() }
}.iconName = "edit-copy-symbolic"
settingsSection.literalButton("delete", I18n["instance.delete"]) {
LauncherEnv.showOkCancel(
I18n["instance.delete.confirm"],
I18n["instance.delete.confirm.title"]
) {
try {
JFiles.deleteRecursive(data.instance.path)
} catch (e: IOException) {
LauncherEnv.showError(I18n["instance.delete.fail"], e)
}
}
}.iconName = "edit-delete-symbolic"
registerForUnbind(data.launch.onClicked { GtkMenubar.launch(data.instance, LaunchType.Client) })
}
override fun UnbindContext.unbind(widget: ActionRow, data: Decomposed) {
data.popoverMenu.insertActionGroup(data.instanceId, null)
}
override fun ActionContext.castWidget(widget: Widget): ActionRow = widget as ActionRow
override fun ActionContext.lookup(id: String, widget: ActionRow): Decomposed {
val instance = instanceList[id]
val prefixes = widget.firstChild!!.firstChild as Box
val suffixes = widget.firstChild!!.lastChild as Box
val thumbnail = InstanceThumbnail.castFrom(prefixes.firstChild as Stack)
val launch = suffixes.firstChild as Button
val menuButton = launch.nextSibling as MenuButton
val popoverMenu = menuButton.popover as PopoverMenu
return Decomposed(listItem, id, instance, thumbnail, launch, popoverMenu)
}
class Decomposed(
val item: ListItem,
val instanceId: String,
val instance: Instance?,
val thumbnail: InstanceThumbnail,
val launch: Button,
val popoverMenu: PopoverMenu
)
}